123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- package tftp
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- )
- const (
- opRRQ = uint16(1) // Read request (RRQ)
- opWRQ = uint16(2) // Write request (WRQ)
- opDATA = uint16(3) // Data
- opACK = uint16(4) // Acknowledgement
- opERROR = uint16(5) // Error
- opOACK = uint16(6) // Options Acknowledgment
- )
- const (
- blockLength = 512
- datagramLength = 516
- )
- type options map[string]string
- // RRQ/WRQ packet
- //
- // 2 bytes string 1 byte string 1 byte
- // --------------------------------------------------
- // | Opcode | Filename | 0 | Mode | 0 |
- // --------------------------------------------------
- type pRRQ []byte
- type pWRQ []byte
- // packRQ returns length of the packet in b
- func packRQ(p []byte, op uint16, filename, mode string, opts options) int {
- binary.BigEndian.PutUint16(p, op)
- n := 2
- n += copy(p[2:len(p)-10], filename)
- p[n] = 0
- n++
- n += copy(p[n:], mode)
- p[n] = 0
- n++
- for name, value := range opts {
- n += copy(p[n:], name)
- p[n] = 0
- n++
- n += copy(p[n:], value)
- p[n] = 0
- n++
- }
- return n
- }
- func unpackRQ(p []byte) (filename, mode string, opts options, err error) {
- bs := bytes.Split(p[2:], []byte{0})
- if len(bs) < 2 {
- return "", "", nil, fmt.Errorf("missing filename or mode")
- }
- filename = string(bs[0])
- mode = string(bs[1])
- if len(bs) < 4 {
- return filename, mode, nil, nil
- }
- opts = make(options)
- for i := 2; i+1 < len(bs); i += 2 {
- opts[string(bs[i])] = string(bs[i+1])
- }
- return filename, mode, opts, nil
- }
- // OACK packet
- //
- // +----------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
- // | Opcode | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
- // +----------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
- type pOACK []byte
- func packOACK(p []byte, opts options) int {
- binary.BigEndian.PutUint16(p, opOACK)
- n := 2
- for name, value := range opts {
- n += copy(p[n:], name)
- p[n] = 0
- n++
- n += copy(p[n:], value)
- p[n] = 0
- n++
- }
- return n
- }
- func unpackOACK(p []byte) (opts options, err error) {
- bs := bytes.Split(p[2:], []byte{0})
- opts = make(options)
- for i := 0; i+1 < len(bs); i += 2 {
- opts[string(bs[i])] = string(bs[i+1])
- }
- return opts, nil
- }
- // ERROR packet
- //
- // 2 bytes 2 bytes string 1 byte
- // ------------------------------------------
- // | Opcode | ErrorCode | ErrMsg | 0 |
- // ------------------------------------------
- type pERROR []byte
- func packERROR(p []byte, code uint16, message string) int {
- binary.BigEndian.PutUint16(p, opERROR)
- binary.BigEndian.PutUint16(p[2:], code)
- n := copy(p[4:len(p)-2], message)
- p[4+n] = 0
- return n + 5
- }
- func (p pERROR) code() uint16 {
- return binary.BigEndian.Uint16(p[2:])
- }
- func (p pERROR) message() string {
- return string(p[4:])
- }
- // DATA packet
- //
- // 2 bytes 2 bytes n bytes
- // ----------------------------------
- // | Opcode | Block # | Data |
- // ----------------------------------
- type pDATA []byte
- func (p pDATA) block() uint16 {
- return binary.BigEndian.Uint16(p[2:])
- }
- // ACK packet
- //
- // 2 bytes 2 bytes
- // -----------------------
- // | Opcode | Block # |
- // -----------------------
- type pACK []byte
- func (p pACK) block() uint16 {
- return binary.BigEndian.Uint16(p[2:])
- }
- func parsePacket(p []byte) (interface{}, error) {
- l := len(p)
- if l < 2 {
- return nil, fmt.Errorf("short packet")
- }
- opcode := binary.BigEndian.Uint16(p)
- switch opcode {
- case opRRQ:
- if l < 4 {
- return nil, fmt.Errorf("short RRQ packet: %d", l)
- }
- return pRRQ(p), nil
- case opWRQ:
- if l < 4 {
- return nil, fmt.Errorf("short WRQ packet: %d", l)
- }
- return pWRQ(p), nil
- case opDATA:
- if l < 4 {
- return nil, fmt.Errorf("short DATA packet: %d", l)
- }
- return pDATA(p), nil
- case opACK:
- if l < 4 {
- return nil, fmt.Errorf("short ACK packet: %d", l)
- }
- return pACK(p), nil
- case opERROR:
- if l < 5 {
- return nil, fmt.Errorf("short ERROR packet: %d", l)
- }
- return pERROR(p), nil
- case opOACK:
- if l < 6 {
- return nil, fmt.Errorf("short OACK packet: %d", l)
- }
- return pOACK(p), nil
- default:
- return nil, fmt.Errorf("unknown opcode: %d", opcode)
- }
- }
|