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.



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

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.



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

def context
  @context
end

#filesObject

Returns the value of attribute files.



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

def files
  @files
end

#incoming_file_hookObject

Returns the value of attribute incoming_file_hook.



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

def incoming_file_hook
  @incoming_file_hook
end

#listen_hostObject

Returns the value of attribute listen_host.



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

def listen_host
  @listen_host
end

#listen_portObject

Returns the value of attribute listen_port.



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

def listen_port
  @listen_port
end

#sockObject

Returns the value of attribute sock.



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

def sock
  @sock
end

#threadObject

Returns the value of attribute thread.



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

def thread
  @thread
end

#transfersObject

Returns the value of attribute transfers.



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

def transfers
  @transfers
end

#uploadedObject

Returns the value of attribute uploaded.



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

def uploaded
  @uploaded
end

Instance Method Details

#find_file(fname) ⇒ Object

Find the hash entry for a file that may be offered



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

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.



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

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



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

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



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

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



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

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



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

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



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

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

#startObject

Start the TFTP server



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

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



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

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