module TFTP::Packet

Packet can parse a binary string into a lightweight object representation.

Constants

ACK

Acknowledgement

DATA

Data

ERROR

Error

RRQ

Read Request

WRQ

Write Request

Public Class Methods

parse(data) click to toggle source

Parse a binary string into a packet. Does some sanity checking, can raise a ParseError.

# File lib/tftp/tftp.rb, line 57
def self.parse(data)
  data = data.force_encoding('ascii-8bit')

  opcode = data.unpack('n').first
  if opcode < 1 || opcode > 5
    raise ParseError, "Unknown packet opcode '#{opcode.inspect}'"
  end

  payload = data.slice(2, data.length - 2)
  case opcode
  when 1, 2 # rrq, wrq
    raise ParseError, 'Not null terminated' if payload.slice(payload.length - 1) != "\x00"
    xs = payload.split("\x00")
    raise ParseError, "Not enough elements: #{xs.inspect}" if xs.length < 2
    filename = xs[0]
    mode = xs[1].downcase.to_sym
    raise ParseError, "Unknown mode '#{xs[1].inspect}'" unless [:netascii, :octet].member? mode
    return RRQ.new(filename, mode) if opcode == 1
    return WRQ.new(filename, mode)
  when 3 # data
    seq = payload.unpack('n').first
    block = payload.slice(2, payload.length - 2) || ''
    raise ParseError, "Exceeded block length with #{block.length} bytes" if block.length > 512
    return DATA.new(seq, block)
  when 4 # ack
    raise ParseError, "Wrong payload length with #{payload.length} bytes" if payload.length != 2
    seq = payload.unpack('n').first
    return ACK.new(seq)
  when 5 # error
    raise ParseError, 'Not null terminated' if payload.slice(payload.length - 1) != "\x00"
    code = payload.unpack('n').first
    raise ParseError, "Unknown error code '#{code.inspect}'" if code < 0 || code > 7
    msg = payload.slice(2, payload.length - 3) || ''
    return ERROR.new(code, msg)
  end
end