class TFTP::Server::Base
Basic server utilizing threads for handling sessions.
It lacks a mutex around access to @clients, in case you'd want to stress test it for 10K or something.
@attr handler [Handler] Session handler @attr address [String] Address to listen to @attr port [Integer] Session dispatcher port @attr clients [Hash] Current sessions
Attributes
Public Class Methods
Initialize the server.
Options:
- :address => address to listen to (default: '0.0.0.0') - :port => dispatcher port (default: 69) - :logger => logger instance
@param handler [Handler] Initialized session handler @param opts [Hash] Options
# File lib/tftp/tftp.rb, line 299 def initialize(handler, opts = {}) @handler = handler @address = opts[:address] || '0.0.0.0' @port = opts[:port] || 69 @logger = opts[:logger] @clients = Hash.new @run = false end
Public Instance Methods
Run the main server loop.
This is obviously blocking.
# File lib/tftp/tftp.rb, line 313 def run! log :info, "UDP server loop at #{@address}:#{@port}" @run = true Socket.udp_server_loop(@address, @port) do |msg, src| break unless @run addr = src.remote_address tag = "[#{addr.ip_address}:#{addr.ip_port.to_s.ljust(5)}]" log :info, "#{tag} New initial packet received" begin pkt = Packet.parse(msg) rescue ParseError => e log :warn, "#{tag} Packet parse error: #{e.to_s}" next end log :debug, "#{tag} -> PKT: #{pkt.inspect}" tid = get_tid tag = "[#{addr.ip_address}:#{addr.ip_port.to_s.ljust(5)}:#{tid.to_s.ljust(5)}]" sock = addr.connect_from(@address, tid) @clients[tid] = tag unless pkt.is_a?(Packet::RRQ) || pkt.is_a?(Packet::WRQ) log :warn, "#{tag} Bad initial packet: #{pkt.class}" sock.send(Packet::ERROR.new(4, 'Illegal TFTP operation.').encode, 0) sock.close next end Thread.new do @handler.run!(tag, pkt, sock, src) @clients.delete(tid) log :info, "#{tag} Session ended" end end log :info, 'UDP server loop has stopped' end
Stop the main server loop.
This will allow the currently pending sessions to finish.
# File lib/tftp/tftp.rb, line 355 def stop log :info, 'Stopping UDP server loop' @run = false UDPSocket.new.send('break', 0, @address, @port) end
Private Instance Methods
Get the server's TID.
The TID is basically a random port number we will use for a session. This actually tries to get a unique TID per session. It uses only ports 1024 - 65535 as not to require root.
# File lib/tftp/tftp.rb, line 367 def get_tid tid = 1024 + rand(64512) tid = 1024 + rand(64512) while @clients.has_key? tid tid end
# File lib/tftp/tftp.rb, line 373 def log(level, msg) @logger.send(level, msg) if @logger end