Class: Rex::Proto::TFTP::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/tftp/server.rb

Overview

TFTP Server class

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port = 69, listen_host = '0.0.0.0', context = {}) ⇒ Server

Returns a new instance of Server.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rex/proto/tftp/server.rb', line 29

def initialize(port = 69, listen_host = '0.0.0.0', context = {})
  self.listen_host = listen_host
  self.listen_port = port
  self.context = context
  self.sock = nil
  @shutting_down = false
  @output_dir = nil
  @tftproot = nil

  self.files = []
  self.uploaded = []
  self.transfers = []
end

Instance Attribute Details

#contextObject

Returns the value of attribute context.



172
173
174
# File 'lib/rex/proto/tftp/server.rb', line 172

def context
  @context
end

#filesObject

Returns the value of attribute files.



173
174
175
# File 'lib/rex/proto/tftp/server.rb', line 173

def files
  @files
end

#incoming_file_hookObject

Returns the value of attribute incoming_file_hook.



176
177
178
# File 'lib/rex/proto/tftp/server.rb', line 176

def incoming_file_hook
  @incoming_file_hook
end

#listen_hostObject

Returns the value of attribute listen_host.



172
173
174
# File 'lib/rex/proto/tftp/server.rb', line 172

def listen_host
  @listen_host
end

#listen_portObject

Returns the value of attribute listen_port.



172
173
174
# File 'lib/rex/proto/tftp/server.rb', line 172

def listen_port
  @listen_port
end

#sockObject

Returns the value of attribute sock.



173
174
175
# File 'lib/rex/proto/tftp/server.rb', line 173

def sock
  @sock
end

#threadObject

Returns the value of attribute thread.



174
175
176
# File 'lib/rex/proto/tftp/server.rb', line 174

def thread
  @thread
end

#transfersObject

Returns the value of attribute transfers.



173
174
175
# File 'lib/rex/proto/tftp/server.rb', line 173

def transfers
  @transfers
end

#uploadedObject

Returns the value of attribute uploaded.



173
174
175
# File 'lib/rex/proto/tftp/server.rb', line 173

def uploaded
  @uploaded
end

Instance Method Details

#find_file(fname) ⇒ Object

Find the hash entry for a file that may be offered



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rex/proto/tftp/server.rb', line 134

def find_file(fname)
  # Files served via register_file() take precedence.
  self.files.each do |f|
    if (fname == f[:name])
      return f
    end
  end

  # Now, if we have a tftproot, see if it can serve from it
  if @tftproot
    return find_file_in_root(fname)
  end

  nil
end

#find_file_in_root(fname) ⇒ Object

Find the file in the specified tftp root and add a temporary entry to the files hash.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rex/proto/tftp/server.rb', line 155

def find_file_in_root(fname)
  fn = ::File.expand_path(::File.join(@tftproot, fname))

  # Don't allow directory traversal
  return nil if fn.index(@tftproot) != 0

  return nil if not ::File.file?(fn) or not ::File.readable?(fn)

  # Read the file contents, and register it as being served once
  data = data = ::File.open(fn, "rb") { |fd| fd.read(fd.stat.size) }
  register_file(fname, data)

  # Return the last file in the array
  return self.files[-1]
end

#register_file(fn, content, once = false) ⇒ Object

Register a filename and content for a client to request



83
84
85
86
87
88
89
# File 'lib/rex/proto/tftp/server.rb', line 83

def register_file(fn, content, once = false)
  self.files << {
    :name => fn,
    :data => content,
    :once => once
  }
end

#send_error(from, num) ⇒ Object

Send an error packet w/the specified code and string



111
112
113
114
115
116
117
118
119
120
# File 'lib/rex/proto/tftp/server.rb', line 111

def send_error(from, num)
  if (num < 1 or num >= ERRCODES.length)
    # ignore..
    return
  end
  pkt = [OpError, num].pack('nn')
  pkt << ERRCODES[num]
  pkt << "\x00"
  send_packet(from, pkt)
end

#send_packet(from, pkt) ⇒ Object

Send a single packet to the specified host



126
127
128
# File 'lib/rex/proto/tftp/server.rb', line 126

def send_packet(from, pkt)
  self.sock.sendto(pkt, from[0], from[1])
end

#set_output_dir(outdir) ⇒ Object

Register a directory to write uploaded files to



103
104
105
# File 'lib/rex/proto/tftp/server.rb', line 103

def set_output_dir(outdir)
  @output_dir = outdir if ::File.directory?(outdir)
end

#set_tftproot(rootdir) ⇒ Object

Register an entire directory to serve files from



95
96
97
# File 'lib/rex/proto/tftp/server.rb', line 95

def set_tftproot(rootdir)
  @tftproot = rootdir if ::File.directory?(rootdir)
end

#startObject

Start the TFTP server



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/rex/proto/tftp/server.rb', line 47

def start
  self.sock = Rex::Socket::Udp.create(
    'LocalHost' => listen_host,
    'LocalPort' => listen_port,
    'Context'   => context
    )

  self.thread = Rex::ThreadFactory.spawn("TFTPServerMonitor", false) {
    monitor_socket
  }
end

#stopObject

Stop the TFTP server



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rex/proto/tftp/server.rb', line 63

def stop
  @shutting_down = true

  # Wait a maximum of 30 seconds for all transfers to finish.
  start = ::Time.now
  while (self.transfers.length > 0)
    ::IO.select(nil, nil, nil, 0.5)
    dur = ::Time.now - start
    break if (dur > 30)
  end

  self.files.clear
  self.thread.kill
  self.sock.close rescue nil # might be closed already
end