mirror of
https://gitee.com/lauix/HFish
synced 2025-05-11 04:18:02 +08:00
191 lines
4.0 KiB
Go
191 lines
4.0 KiB
Go
package libs
|
|
|
|
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)
|
|
}
|
|
}
|