netshovel/packet.go

253 lines
6.1 KiB
Go
Raw Normal View History

package netshovel
2018-07-23 09:58:31 -06:00
import (
"encoding/binary"
2018-07-23 17:48:14 -06:00
"encoding/hex"
2018-07-23 09:58:31 -06:00
"fmt"
2018-07-24 17:53:06 -06:00
"github.com/dirtbags/netshovel/gapstring"
2018-07-23 09:58:31 -06:00
"strings"
"time"
)
2018-07-24 17:01:04 -06:00
// Error returned by convenience methods that are unable to get enough data
type ShortError struct {
2018-07-24 17:53:06 -06:00
Wanted int // How many bytes you needed
2018-07-24 17:32:08 -06:00
Available int // How many bytes were available
}
2018-07-24 17:53:06 -06:00
func (e *ShortError) Error() string {
return fmt.Sprintf("Short read: wanted %d of %d available", e.wanted, e.available)
}
2018-07-24 17:01:04 -06:00
// Error returned by convenience methods that are unable to operate on gaps in data
type MissingError struct {
}
2018-07-24 17:53:06 -06:00
func (e *MissingError) Error() string {
return "Operation on missing bytes"
}
2018-07-24 17:01:04 -06:00
// A Key,Value Pair
type namedField struct {
2018-07-23 17:48:14 -06:00
key, value string
}
2018-07-24 17:01:04 -06:00
// An application protocol header field
type headerField struct {
2018-07-24 17:53:06 -06:00
name string
bits int
value interface{}
order binary.ByteOrder
}
2018-07-24 17:01:04 -06:00
// A Packet represents a single application-layer packet
//
// The Packet struct provides helper methods to assist
// with
// reverse-engineering new protocols
// and
// documenting header structure.
2018-07-23 09:58:31 -06:00
type Packet struct {
2018-07-24 17:53:06 -06:00
Opcode int
2018-07-23 09:58:31 -06:00
Description string
2018-07-24 17:53:06 -06:00
When time.Time
Payload gapstring.GapString
header []headerField
fields []namedField
2018-07-23 09:58:31 -06:00
}
var never = time.Unix(0, 0)
2018-07-24 17:01:04 -06:00
// Return a new packet
2018-07-23 09:58:31 -06:00
func NewPacket() Packet {
return Packet{
2018-07-24 17:53:06 -06:00
Opcode: -1,
2018-07-23 09:58:31 -06:00
Description: "Undefined",
2018-07-24 17:53:06 -06:00
When: never,
Payload: gapstring.GapString{},
header: []headerField{},
fields: []namedField{},
2018-07-23 09:58:31 -06:00
}
}
2018-07-24 17:01:04 -06:00
// Return a multi-line string describing this packet
//
// This shows the timestamp, opcode, description, and hex dump.
// If you set any values, those are displayed in the order they were set.
2018-07-23 09:58:31 -06:00
func (pkt *Packet) Describe() string {
out := new(strings.Builder)
2018-07-23 17:48:14 -06:00
fmt.Fprintf(out, " %s Opcode %d: %s\n",
2018-07-24 17:53:06 -06:00
pkt.When.UTC().Format(time.RFC3339Nano),
2018-07-23 09:58:31 -06:00
pkt.Opcode,
pkt.Description,
)
2018-07-24 17:53:06 -06:00
for _, f := range pkt.Fields {
2018-07-23 17:48:14 -06:00
fmt.Fprintf(out, " %s: %s\n", f.key, f.value)
2018-07-23 15:34:22 -06:00
}
fmt.Fprint(out, pkt.Payload.Hexdump())
return out.String()
}
2018-07-24 17:01:04 -06:00
// Set a value
//
// This is intended to be used to note debugging information
// that you'd like to see on each packet.
2018-07-23 15:34:22 -06:00
func (pkt *Packet) Set(key, value string) {
2018-07-24 17:01:04 -06:00
pkt.fields = append(pkt.fields, namedField{key, value})
2018-07-23 17:48:14 -06:00
}
2018-07-24 17:01:04 -06:00
// Set a string value, displaying its Go string representation
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetString(key, value string) {
pkt.Set(key, fmt.Sprintf("%#v", value))
}
2018-07-24 17:01:04 -06:00
// Set an int value, displaying its decimal and hexadecimal representations
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetInt(key string, value int) {
pkt.Set(key, fmt.Sprintf("%d == 0x%x", value, value))
}
2018-07-24 17:01:04 -06:00
// Set an unsigned int value, displaying its decimal and hexadecimal representations
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetUint(key string, value uint) {
pkt.Set(key, fmt.Sprintf("%d == 0x%x", value, value))
}
2018-07-24 17:01:04 -06:00
// Set an Unt32 value, displaying its decimal and 0-padded hexadecimal representations
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetUint32(key string, value uint32) {
pkt.Set(key, fmt.Sprintf("%d == 0x%04x", value, value))
}
2018-07-24 17:01:04 -06:00
// Set an []byte value, displaying the hex encoding of the bytes
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetBytes(key string, value []byte) {
pkt.Set(key, hex.EncodeToString(value))
}
2018-07-24 17:01:04 -06:00
// Set a GapString value, displaying the hex encoding and runes encoding (like a hex dump)
2018-07-23 17:48:14 -06:00
func (pkt *Packet) SetGapString(key string, value gapstring.GapString) {
pkt.Set(key, fmt.Sprintf("%s %s", value.HexString(), value.Runes()))
2018-07-23 09:58:31 -06:00
}
2018-07-24 17:01:04 -06:00
// Peel octets bytes off of the Payload, returning those bytes
func (pkt *Packet) Peel(octets int) ([]byte, error) {
pllen := pkt.Payload.Length()
if octets > pllen {
return nil, &ShortError{octets, pllen}
}
buf := pkt.Payload.Slice(0, octets)
if buf.Missing() > 0 {
return nil, &MissingError{}
}
pkt.Payload = pkt.Payload.Slice(octets, pkt.Payload.Length())
b := buf.Bytes()
return b, nil
}
2018-07-24 17:01:04 -06:00
// Add a field to the header field description
func (pkt *Packet) AddHeaderField(order binary.ByteOrder, name string, bits int, value interface{}) {
2018-07-24 17:01:04 -06:00
h := headerField{
2018-07-24 17:53:06 -06:00
name: name,
bits: bits,
value: value,
order: order,
}
2018-07-24 17:01:04 -06:00
pkt.header = append(pkt.header, h)
}
2018-07-24 17:01:04 -06:00
// Peel from Payload an unsigned integer of size bits, adding it to the header field list
func (pkt *Packet) readUint(order binary.ByteOrder, bits int, name string) (interface{}, error) {
switch bits {
case 8:
case 16:
case 32:
case 64:
default:
2018-07-24 17:53:06 -06:00
return 0, fmt.Errorf("Weird number of bits: %d", bits)
}
2018-07-24 17:53:06 -06:00
octets := bits >> 3
b, err := pkt.Peel(octets)
if err != nil {
return 0, err
}
2018-07-24 17:53:06 -06:00
var value interface{}
switch bits {
case 8:
2018-07-24 17:53:06 -06:00
value = b[0]
case 16:
value = order.Uint16(b)
case 32:
2018-07-24 17:53:06 -06:00
value = order.Uint32(b)
case 64:
2018-07-24 17:53:06 -06:00
value = order.Uint64(b)
}
2018-07-24 17:01:04 -06:00
pkt.AddheaderField(order, name, bits, value)
2018-07-24 17:53:06 -06:00
return value, nil
}
2018-07-24 17:01:04 -06:00
// Peel off a uint64, little-endian
func (pkt *Packet) Uint64LE(name string) (uint64, error) {
value, err := pkt.readUint(binary.LittleEndian, 64, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint64), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint32, little-endian
func (pkt *Packet) Uint32LE(name string) (uint32, error) {
value, err := pkt.readUint(binary.LittleEndian, 32, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint32), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint16, little-endian
func (pkt *Packet) Uint16LE(name string) (uint16, error) {
value, err := pkt.readUint(binary.LittleEndian, 16, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint16), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint64, big-endian
func (pkt *Packet) Uint64BE(name string) (uint64, error) {
value, err := pkt.readUint(binary.BigEndian, 64, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint64), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint32, big-endian
func (pkt *Packet) Uint32BE(name string) (uint32, error) {
value, err := pkt.readUint(binary.BigEndian, 32, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint32), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint16, big-endian
func (pkt *Packet) Uint16BE(name string) (uint16, error) {
value, err := pkt.readUint(binary.BigEndian, 16, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint16), err
}
2018-07-24 17:01:04 -06:00
// Peel off a uint8 (aka byte)
func (pkt *Packet) Uint8(name string) (uint8, error) {
value, err := pkt.readUint(binary.BigEndian, 8, name)
2018-07-24 10:58:40 -06:00
if err != nil {
return 0, err
}
return value.(uint8), err
}