Module: TFTP::Packet

Defined in:
lib/tftp/tftp.rb

Overview

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

Defined Under Namespace

Classes: ACK, Base, DATA, ERROR, RRQ, WRQ

Class Method Summary collapse

Class Method Details

.parse(data) ⇒ Object

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



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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