Tuesday, March 24, 2009

TFTP Packet Factory

Straightforward, but fun.


class PacketFactory # TFTP = RFC 1350, 2347, 2348 (not needed here)
# #
# 2 bytes string 1b string 1b string 1b string 1b ... #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# | 01 |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# string 1b string 1b #
# >-------+---+---~~---+---+ #
# < optN | 0 | valueN | 0 | #
# >-------+---+---~~---+---+ #
# #
# RRQ packet #
# #
# The mode field contains the string "netascii", "octet", or "mail" #
# #
def buildRRQ( filename, mode, *opts )
packet = "\0\1" + filename + "\0" + mode + "\0"
opts.each { |o| packet += o + "\0" }
return packet
end

# #
# 2 bytes string 1b string 1b string 1b string 1b ... #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# | 02 |filename| 0 | mode | 0 | opt1 | 0 | value1 | 0 | < #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+--> #
# string 1b string 1b #
# >-------+---+---~~---+---+ #
# < optN | 0 | valueN | 0 | #
# >-------+---+---~~---+---+ #
# #
# WRQ packet #
# #
def buildWRQ( filename, mode, blocksize, octets )
packet = "\0\2" + filename + "\0"
+ mode + "\0"
+ blocksize + "\0"
+ octets + "\0"
return packet
end
# #
# 2 bytes 2 bytes n bytes #
# ---------------------------------- #
# | 03 | Block # | Data | #
# ---------------------------------- #
# #
# DATA packet #
# #
def buildDAT( blocknum, data )
return "\0\3" + [blocknum, data].flatten.pack("n*")
end
# #
# 2 bytes 2 bytes #
# --------------------- #
# | 04 | Block # | #
# --------------------- #
# #
# ACK packet #
# #
def buildACK( blocknum ) return "\0\4" + [blocknum].pack("n") end

def buildACKfromDAT( packet )
packet = packet[0..3]
packet[1] = "\04"
return packet
end
# #
# 2 bytes string 1b string 1b...string 1b string 1b #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ #
# | 06 | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | #
# +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ #
# #
# OACK packet [RFC 2347] #
# #
def buildOACK( opt_hash )
packet = "\0\6"
opt_hash.each_pair { |k,v| packet += k + "\0" + v + "\0" }
return packet
end

# #
# 2 bytes 2 bytes string 1 byte #
# ----------------------------------------- #
# | 05 | ErrorCode | ErrMsg | 0 | #
# ----------------------------------------- #
# #
# ERROR packet #
# #
# Error Codes #
# #
# Value Meaning #
# #
# 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. #
# 8 Invalid option value requested. [RFC 2347] #
# #
def buildERR( errcode, errmsg )
packet = "\0\5" + [errcode, errmsg].pack("na*x")
return packet
end
end

2 comments:

  1. Interesting article about what can go terribly wrong... http://en.wikipedia.org/wiki/Sorcerer%27s_Apprentice_Syndrome

    ReplyDelete
  2. Happily, that terrible side effect was alleviated in the RFCs... but yes, I was also horrified to read about that.

    ReplyDelete