forked from gopacket/gopacket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
writer.go
233 lines (217 loc) · 8.83 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
import (
"fmt"
)
// SerializableLayer allows its implementations to be written out as a set of bytes,
// so those bytes may be sent on the wire or otherwise used by the caller.
// SerializableLayer is implemented by certain Layer types, and can be encoded to
// bytes using the LayerWriter object.
type SerializableLayer interface {
// SerializeTo writes this layer to a slice, growing that slice if necessary
// to make it fit the layer's data.
// Args:
// b: SerializeBuffer to write this layer on to. When called, b.Bytes()
// is the payload this layer should wrap, if any. Note that this
// layer can either prepend itself (common), append itself
// (uncommon), or both (sometimes padding or footers are required at
// the end of packet data). It's also possible (though probably very
// rarely needed) to overwrite any bytes in the current payload.
// After this call, b.Bytes() should return the byte encoding of
// this layer wrapping the original b.Bytes() payload.
// opts: options to use while writing out data.
// Returns:
// error if a problem was encountered during encoding. If an error is
// returned, the bytes in data should be considered invalidated, and
// not used.
//
// SerializeTo calls SHOULD entirely ignore LayerContents and
// LayerPayload. It just serializes based on struct fields, neither
// modifying nor using contents/payload.
SerializeTo(b SerializeBuffer, opts SerializeOptions) error
// LayerType returns the type of the layer that is being serialized to the buffer
LayerType() LayerType
}
// SerializeOptions provides options for behaviors that SerializableLayers may want to
// implement.
type SerializeOptions struct {
// FixLengths determines whether, during serialization, layers should fix
// the values for any length field that depends on the payload.
FixLengths bool
// ComputeChecksums determines whether, during serialization, layers
// should recompute checksums based on their payloads.
ComputeChecksums bool
}
// SerializeBuffer is a helper used by gopacket for writing out packet layers.
// SerializeBuffer starts off as an empty []byte. Subsequent calls to PrependBytes
// return byte slices before the current Bytes(), AppendBytes returns byte
// slices after.
//
// Byte slices returned by PrependBytes/AppendBytes are NOT zero'd out, so if
// you want to make sure they're all zeros, set them as such.
//
// SerializeBuffer is specifically designed to handle packet writing, where unlike
// with normal writes it's easier to start writing at the inner-most layer and
// work out, meaning that we often need to prepend bytes. This runs counter to
// typical writes to byte slices using append(), where we only write at the end
// of the buffer.
//
// It can be reused via Clear. Note, however, that a Clear call will invalidate the
// byte slices returned by any previous Bytes() call (the same buffer is
// reused).
//
// 1. Reusing a write buffer is generally much faster than creating a new one,
// and with the default implementation it avoids additional memory allocations.
// 2. If a byte slice from a previous Bytes() call will continue to be used,
// it's better to create a new SerializeBuffer.
//
// The Clear method is specifically designed to minimize memory allocations for
// similar later workloads on the SerializeBuffer. IE: if you make a set of
// Prepend/Append calls, then clear, then make the same calls with the same
// sizes, the second round (and all future similar rounds) shouldn't allocate
// any new memory.
type SerializeBuffer interface {
// Bytes returns the contiguous set of bytes collected so far by Prepend/Append
// calls. The slice returned by Bytes will be modified by future Clear calls,
// so if you're planning on clearing this SerializeBuffer, you may want to copy
// Bytes somewhere safe first.
Bytes() []byte
// PrependBytes returns a set of bytes which prepends the current bytes in this
// buffer. These bytes start in an indeterminate state, so they should be
// overwritten by the caller. The caller must only call PrependBytes if they
// know they're going to immediately overwrite all bytes returned.
PrependBytes(num int) ([]byte, error)
// AppendBytes returns a set of bytes which appends the current bytes in this
// buffer. These bytes start in an indeterminate state, so they should be
// overwritten by the caller. The caller must only call AppendBytes if they
// know they're going to immediately overwrite all bytes returned.
AppendBytes(num int) ([]byte, error)
// Clear resets the SerializeBuffer to a new, empty buffer. After a call to clear,
// the byte slice returned by any previous call to Bytes() for this buffer
// should be considered invalidated.
Clear() error
// Layers returns all the Layers that have been successfully serialized into this buffer
// already.
Layers() []LayerType
// PushLayer adds the current Layer to the list of Layers that have been serialized
// into this buffer.
PushLayer(LayerType)
}
type serializeBuffer struct {
data []byte
start int
prepended, appended int
layers []LayerType
}
// NewSerializeBuffer creates a new instance of the default implementation of
// the SerializeBuffer interface.
func NewSerializeBuffer() SerializeBuffer {
return &serializeBuffer{}
}
// NewSerializeBufferExpectedSize creates a new buffer for serialization, optimized for an
// expected number of bytes prepended/appended. This tends to decrease the
// number of memory allocations made by the buffer during writes.
func NewSerializeBufferExpectedSize(expectedPrependLength, expectedAppendLength int) SerializeBuffer {
return &serializeBuffer{
data: make([]byte, expectedPrependLength, expectedPrependLength+expectedAppendLength),
start: expectedPrependLength,
prepended: expectedPrependLength,
appended: expectedAppendLength,
}
}
func (w *serializeBuffer) Bytes() []byte {
return w.data[w.start:]
}
func (w *serializeBuffer) PrependBytes(num int) ([]byte, error) {
if num < 0 {
panic("num < 0")
}
if w.start < num {
toPrepend := w.prepended
if toPrepend < num {
toPrepend = num
}
w.prepended += toPrepend
length := cap(w.data) + toPrepend
newData := make([]byte, length)
newStart := w.start + toPrepend
copy(newData[newStart:], w.data[w.start:])
w.start = newStart
w.data = newData[:toPrepend+len(w.data)]
}
w.start -= num
return w.data[w.start : w.start+num], nil
}
func (w *serializeBuffer) AppendBytes(num int) ([]byte, error) {
if num < 0 {
panic("num < 0")
}
initialLength := len(w.data)
if cap(w.data)-initialLength < num {
toAppend := w.appended
if toAppend < num {
toAppend = num
}
w.appended += toAppend
newData := make([]byte, cap(w.data)+toAppend)
copy(newData[w.start:], w.data[w.start:])
w.data = newData[:initialLength]
}
// Grow the buffer. We know it'll be under capacity given above.
w.data = w.data[:initialLength+num]
return w.data[initialLength:], nil
}
func (w *serializeBuffer) Clear() error {
w.start = w.prepended
w.data = w.data[:w.start]
w.layers = w.layers[:0]
return nil
}
func (w *serializeBuffer) Layers() []LayerType {
return w.layers
}
func (w *serializeBuffer) PushLayer(l LayerType) {
w.layers = append(w.layers, l)
}
// SerializeLayers clears the given write buffer, then writes all layers into it so
// they correctly wrap each other. Note that by clearing the buffer, it
// invalidates all slices previously returned by w.Bytes()
//
// Example:
//
// buf := gopacket.NewSerializeBuffer()
// opts := gopacket.SerializeOptions{}
// gopacket.SerializeLayers(buf, opts, a, b, c)
// firstPayload := buf.Bytes() // contains byte representation of a(b(c))
// gopacket.SerializeLayers(buf, opts, d, e, f)
// secondPayload := buf.Bytes() // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf.
func SerializeLayers(w SerializeBuffer, opts SerializeOptions, layers ...SerializableLayer) error {
w.Clear()
for i := len(layers) - 1; i >= 0; i-- {
layer := layers[i]
err := layer.SerializeTo(w, opts)
if err != nil {
return err
}
w.PushLayer(layer.LayerType())
}
return nil
}
// SerializePacket is a convenience function that calls SerializeLayers
// on packet's Layers().
// It returns an error if one of the packet layers is not a SerializableLayer.
func SerializePacket(buf SerializeBuffer, opts SerializeOptions, packet Packet) error {
sls := []SerializableLayer{}
for _, layer := range packet.Layers() {
sl, ok := layer.(SerializableLayer)
if !ok {
return fmt.Errorf("layer %s is not serializable", layer.LayerType().String())
}
sls = append(sls, sl)
}
return SerializeLayers(buf, opts, sls...)
}