class TFTP::Transfer

An in-progress file transfer operation Holds all the state which a TFTP server/client needs to track for a single transfer Subclasses contain logic specific to client downloads, server downloads, client uploads, server uploads

Constants

BASE_RETRANSMIT_TIMEOUT
MAX_RETRANSMIT_TIMEOUT

Attributes

block_no[RW]
buffer[RW]
peer_addr[R]
peer_port[R]
timeout[RW]
timer[RW]

Public Class Methods

new(connection, peer_addr, peer_port, listener) click to toggle source
# File lib/em-tftp.rb, line 111
def initialize(connection, peer_addr, peer_port, listener)
  # 'connection' is the UDP socket used for this transfer (actually an EventMachine::Connection object)
  # each transfer uses a newly opened UDP socket, which is closed after the transfer is finished
  # this is because the UDP port number doubles as a TFTP transfer ID
  # so using the same port number for 2 successive requests to the same peer may cause problems
  @connection = connection
  @peer_addr, @peer_port, @listener = peer_addr, peer_port, listener
  @buffer = @block_no = @timer = nil
  @timeout = 1.5
end

Public Instance Methods

abort!(code=0, error_msg=Protocol::ERROR_MESSAGES[code]) click to toggle source

Abort the file transfer. An optional error message and code can be included. This should be called if the transfer cannot be completed due to a full hard disk, wrong permissions, etc

TFTP error codes include:

0 Not defined, see error message (if any). 1 File not found. 2 Access violation. 3 Disk full or allocation exceeded. 4 Illegal TFTP operation. 5 Unknown transfer ID. 6 File already exists. 7 No such user.

# File lib/em-tftp.rb, line 138
def abort!(code=0, error_msg=Protocol::ERROR_MESSAGES[code])
  stop_timer!
  send_error(code, error_msg || "Unknown error")
  @connection.close_connection_after_writing
  @listener.failed(error_msg || "Unknown error")
end
error!(error_msg) click to toggle source
# File lib/em-tftp.rb, line 145
def error!(error_msg)
  stop_timer!
  @connection.close_connection
  @listener.failed(error_msg)
end

Private Instance Methods

finished!() click to toggle source
# File lib/em-tftp.rb, line 171
def finished!
  stop_timer!
  @connection.close_connection
  @listener.completed
end
rrq(packet, port) click to toggle source
# File lib/em-tftp.rb, line 183
def rrq(packet, port)
  # a transfer has already started and is not yet finished
  # a RRQ/WRQ shouldn't arrive at this time
  abort!(4, "Received unexpected TFTP RRQ packet")
end
send_packet(addr=@peer_addr, port=@peer_port, packet_data) click to toggle source
# File lib/em-tftp.rb, line 177
def send_packet(addr=@peer_addr, port=@peer_port, packet_data)
  $stderr.puts "Sending: #{packet_data} (#{packet_data[0..3].bytes.to_a.join(',')}) to #{addr}:#{port}" if $DEBUG
  @connection.send_datagram(packet_data, addr, port)
  set_timer!(packet_data) unless packet_data.start_with? "\0\5" # no timeout and retransmit for error packets
end
set_timer!(data) click to toggle source
# File lib/em-tftp.rb, line 160
def set_timer!(data)
  @timer = EM::Timer.new(@timeout) do
    @timeout *= 2
    if @timeout <= MAX_RETRANSMIT_TIMEOUT
      send_packet(data)
    else
      abort!(0, "Connection timed out")
    end
  end
end
stop_timer!() click to toggle source
# File lib/em-tftp.rb, line 153
def stop_timer!
  if @timer
    @timer.cancel
    @timer = nil
  end
  @timeout = BASE_RETRANSMIT_TIMEOUT
end
wrq(packet, port) click to toggle source
# File lib/em-tftp.rb, line 188
def wrq(packet, port)
  abort!(4, "Received unexpected TFTP WRQ packet")
end