Neale Pickett
·
2020-09-24
packet.go
1package netshovel
2
3import (
4 "encoding/binary"
5 "encoding/hex"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/dirtbags/netshovel/gapstring"
11)
12
13// ShortError is returned by convenience methods that are unable to get enough data
14type ShortError struct {
15 Wanted int // How many bytes you needed
16 Available int // How many bytes were available
17}
18
19func (e *ShortError) Error() string {
20 return fmt.Sprintf("Short read: wanted %d of %d available", e.Wanted, e.Available)
21}
22
23// MissingError is returned by convenience methods that are unable to operate on gaps in data
24type MissingError struct {
25}
26
27func (e *MissingError) Error() string {
28 return "Operation on missing bytes"
29}
30
31// A Key,Value Pair
32type namedField struct {
33 key, value string
34}
35
36// An application protocol header field
37type headerField struct {
38 name string
39 bits int
40 value interface{}
41 order binary.ByteOrder
42}
43
44// A Packet represents a single application-layer packet
45//
46// The Packet struct provides helper methods to assist
47// with
48// reverse-engineering new protocols
49// and
50// documenting header structure.
51type Packet struct {
52 Opcode int
53 Description string
54 When time.Time
55 Payload gapstring.GapString
56 header []headerField
57 fields []namedField
58}
59
60var never = time.Unix(0, 0)
61
62// NewPacket returns a new packet
63func NewPacket() Packet {
64 return Packet{
65 Opcode: -1,
66 Description: "Undefined",
67 When: never,
68 Payload: gapstring.GapString{},
69 header: []headerField{},
70 fields: []namedField{},
71 }
72}
73
74// DescribeType returns a string with timestamp, opcode, and description of this packet
75func (pkt *Packet) DescribeType() string {
76 return fmt.Sprintf(
77 " %s Opcode %d: %s",
78 pkt.When.UTC().Format(time.RFC3339Nano),
79 pkt.Opcode,
80 pkt.Description,
81 )
82}
83
84// DescribeFields returns a multi-line string describing fields in this packet
85func (pkt *Packet) DescribeFields() string {
86 out := new(strings.Builder)
87 for _, f := range pkt.fields {
88 fmt.Fprintf(out, " %s: %s\n", f.key, f.value)
89 }
90 return out.String()
91}
92
93func center(s string, w int) string {
94 if w < 3 {
95 return "?"
96 }
97 if len(s) > w {
98 s = s[0:w-3] + "…"
99 }
100 return fmt.Sprintf("%*s", -w, fmt.Sprintf("%*s", (w+len(s))/2, s))
101}
102
103// DescribeHeader returns a multi-line string describing this packet's header structure
104func (pkt *Packet) DescribeHeader() string {
105 out := new(strings.Builder)
106 out.WriteString(" 0 1\n")
107 out.WriteString(" 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f\n")
108 out.WriteString("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")
109
110 bitOffset := 0
111 for _, f := range pkt.header {
112 bits := f.bits
113 for bits > 0 {
114 linebits := bits
115 if linebits+bitOffset > 0x20 {
116 linebits = 0x20 - bitOffset
117 }
118
119 // Generate centered string
120 val := fmt.Sprintf("0x%x", f.value)
121 nameval := f.name
122 if f.bits == bits {
123 out.WriteString("|")
124 } else {
125 out.WriteString(" ")
126 val = ""
127 nameval = "..."
128 }
129 out.WriteString(center(nameval, linebits*2-len(val)-2))
130 out.WriteString(val)
131 out.WriteString(" ")
132
133 bitOffset += linebits
134 bits -= linebits
135 if bitOffset == 0x20 {
136 if bits == 0 {
137 out.WriteString("|")
138 } else {
139 out.WriteString(" ")
140 }
141 out.WriteString("\n")
142 out.WriteString("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")
143 bitOffset = 0
144 }
145 }
146 }
147 if bitOffset > 0 {
148 out.WriteString("|\n")
149 for o := 0; o < bitOffset; o++ {
150 out.WriteString("+-")
151 }
152 out.WriteString("+\n")
153 }
154 return out.String()
155}
156
157// Describe returns a multi-line string describing this packet
158//
159// This shows the timestamp, opcode, description, and hex dump.
160// If you set any values, those are displayed in the order they were set.
161//
162// This will quickly get unweildy, especially for large conversations.
163// You are encouraged to implement your own Describe() method.
164func (pkt *Packet) Describe() string {
165 out := new(strings.Builder)
166
167 fmt.Fprintln(out, pkt.DescribeType())
168 fmt.Fprint(out, pkt.DescribeFields())
169 fmt.Fprint(out, pkt.DescribeHeader())
170 fmt.Fprint(out, pkt.Payload.Hexdump())
171 return out.String()
172}
173
174// Set a value
175//
176// This is intended to be used to note debugging information
177// that you'd like to see on each packet.
178func (pkt *Packet) Set(key, value string) {
179 pkt.fields = append(pkt.fields, namedField{key, value})
180}
181
182// SetString sets a string value, displaying its Go string representation
183func (pkt *Packet) SetString(key, value string) {
184 pkt.Set(key, fmt.Sprintf("%#v", value))
185}
186
187// SetInt sets an int value, displaying its decimal and hexadecimal representations
188func (pkt *Packet) SetInt(key string, value int) {
189 pkt.Set(key, fmt.Sprintf("%d == 0x%x", value, value))
190}
191
192// SetUint sets an unsigned int value, displaying its decimal and hexadecimal representations
193func (pkt *Packet) SetUint(key string, value uint) {
194 pkt.Set(key, fmt.Sprintf("%d == 0x%x", value, value))
195}
196
197// SetUint32 sets an Unt32 value, displaying its decimal and 0-padded hexadecimal representations
198func (pkt *Packet) SetUint32(key string, value uint32) {
199 pkt.Set(key, fmt.Sprintf("%d == 0x%04x", value, value))
200}
201
202// SetBytes sets a []byte value, displaying the hex encoding of the bytes
203func (pkt *Packet) SetBytes(key string, value []byte) {
204 pkt.Set(key, hex.EncodeToString(value))
205}
206
207// SetGapString sets a GapString value, displaying the hex encoding and runes encoding (like a hex dump)
208func (pkt *Packet) SetGapString(key string, value gapstring.GapString) {
209 pkt.Set(key, fmt.Sprintf("%s %s", value.HexString(), value.Runes()))
210}
211
212// Peel octets bytes off of the Payload, returning those bytes
213func (pkt *Packet) Peel(octets int) ([]byte, error) {
214 pllen := pkt.Payload.Length()
215 if octets > pllen {
216 return nil, &ShortError{octets, pllen}
217 }
218 buf := pkt.Payload.Slice(0, octets)
219 if buf.Missing() > 0 {
220 return nil, &MissingError{}
221 }
222
223 pkt.Payload = pkt.Payload.Slice(octets, pkt.Payload.Length())
224 b := buf.Bytes()
225 return b, nil
226}
227
228// AddHeaderField adds a field to the header field description
229func (pkt *Packet) AddHeaderField(order binary.ByteOrder, name string, bits int, value interface{}) {
230 h := headerField{
231 name: name,
232 bits: bits,
233 value: value,
234 order: order,
235 }
236 pkt.header = append(pkt.header, h)
237}
238
239// Peel from Payload an unsigned integer of size bits, adding it to the header field list
240func (pkt *Packet) readUint(order binary.ByteOrder, bits int, name string) (interface{}, error) {
241 switch bits {
242 case 8:
243 case 16:
244 case 32:
245 case 64:
246 default:
247 return 0, fmt.Errorf("Weird number of bits: %d", bits)
248 }
249
250 octets := bits >> 3
251 b, err := pkt.Peel(octets)
252 if err != nil {
253 return 0, err
254 }
255
256 var value interface{}
257 switch bits {
258 case 8:
259 value = b[0]
260 case 16:
261 value = order.Uint16(b)
262 case 32:
263 value = order.Uint32(b)
264 case 64:
265 value = order.Uint64(b)
266 }
267 pkt.AddHeaderField(order, name, bits, value)
268
269 return value, nil
270}
271
272// Uint64LE peels off a uint64, little-endian
273func (pkt *Packet) Uint64LE(name string) (uint64, error) {
274 value, err := pkt.readUint(binary.LittleEndian, 64, name)
275 if err != nil {
276 return 0, err
277 }
278 return value.(uint64), err
279}
280
281// Uint32LE peels off a uint32, little-endian
282func (pkt *Packet) Uint32LE(name string) (uint32, error) {
283 value, err := pkt.readUint(binary.LittleEndian, 32, name)
284 if err != nil {
285 return 0, err
286 }
287 return value.(uint32), err
288}
289
290// Uint16LE peels off a uint16, little-endian
291func (pkt *Packet) Uint16LE(name string) (uint16, error) {
292 value, err := pkt.readUint(binary.LittleEndian, 16, name)
293 if err != nil {
294 return 0, err
295 }
296 return value.(uint16), err
297}
298
299// Uint64BE peels off a uint64, big-endian
300func (pkt *Packet) Uint64BE(name string) (uint64, error) {
301 value, err := pkt.readUint(binary.BigEndian, 64, name)
302 if err != nil {
303 return 0, err
304 }
305 return value.(uint64), err
306}
307
308// Uint32BE peels off a uint32, big-endian
309func (pkt *Packet) Uint32BE(name string) (uint32, error) {
310 value, err := pkt.readUint(binary.BigEndian, 32, name)
311 if err != nil {
312 return 0, err
313 }
314 return value.(uint32), err
315}
316
317// Uint16BE peels off a uint16, big-endian
318func (pkt *Packet) Uint16BE(name string) (uint16, error) {
319 value, err := pkt.readUint(binary.BigEndian, 16, name)
320 if err != nil {
321 return 0, err
322 }
323 return value.(uint16), err
324}
325
326// Uint8 peels off a uint8 (aka byte)
327func (pkt *Packet) Uint8(name string) (uint8, error) {
328 value, err := pkt.readUint(binary.BigEndian, 8, name)
329 if err != nil {
330 return 0, err
331 }
332 return value.(uint8), err
333}