-
Notifications
You must be signed in to change notification settings - Fork 0
/
header.go
387 lines (313 loc) · 12.6 KB
/
header.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package sap
import (
"encoding/binary"
"io"
"mime"
"net"
)
// Header represents an SAP packet header
type Header struct {
// The version number field MUST be set to 1
Version uint8
// If the A bit is 0, the originating source field contains a 32-bit IPv4 address.
// If the A bit is 1, the originating source contains a 128-bit IPv6 address.
AddressType AddressType
// SAP announcers MUST set this to 0, SAP listeners MUST ignore the contents of this field.
Reserved uint8
// If this bit is set to 0 this is a session announcement packet
// If this bit is set to 1 this is a session deletion packet.
MessageType MessageType
// If the encryption bit is set to 1, the payload of the SAP packet is encrypted.
// If this bit is 0 the packet is not encrypted.
Encrypted uint8
// If the compressed bit is set to 1, the payload is
// compressed using the zlib compression algorithm (https://datatracker.ietf.org/doc/html/rfc2974#ref-3)
Compressed uint8
// An 8 bit unsigned quantity giving the number of 32 bit words following the main SAP header that contain aauthentication data.
// If it is zero, no authentication header is present.
AuthenticationLength uint8
// Digital signature of the packet, with length as specified by the authentication length header field.
AuthenticationData []uint32
// A 16 bit quantity that, used in combination with the originating source, provides a globally unique identifier
// indicating the precise version of this announcement.
//
// It MUST be unique for each session announced by a particular SAP announcer
// and it MUST be changed if the session description is modified
// (and a session deletion message SHOULD be sent for the old version of the session).
//
// SAP listeners MAY silently discard messages if the message
// identifier hash is set to zero.
MessageIDHash uint16
// This gives the IP address of the original source
// of the message. This is an IPv4 address if the A field is set to
// zero, else it is an IPv6 address. The address is stored in
// network byte order.
OriginatingSource net.IP
// The payload type field is a MIME content type specifier, describing the format of the payload.
//
// This is a variable length ASCII text string, followed by a single zero byte (ASCII NUL).
// The payload type SHOULD be included in all packets.
// If the payload type is `application/sdp' both the payload type and its terminating zero byte MAY be omitted
//
// Technically, it is part of the Payload, but makes more sense to parse it with the rest of the header
PayloadType string
}
// If this is an announcement or deletion packet
type MessageType uint8
const (
Announcement MessageType = 0
Deletion MessageType = 1
)
// If the origination source is from an IPv4 or IPv6 address
type AddressType uint8
const (
IPv4 AddressType = 0
IPv6 AddressType = 1
)
const (
versionShift = 5 // Number of bits to shift for the version bit
addressShift = 4 // Number of bits to shift for the address type bit
reservedShift = 3 // Number of bits to shift for the reserved bit
messageTypeShift = 2 // Number of bits to shift for the message type bit
encryptedShift = 1 // Number of bits to shift for the encrypted bit
compressedShift = 0 // Number of bits to shift for the compressed bit
oneBitMask = 0x01 // Mask to shift 1 bit
)
// MarshalSize returns the size of the header once marshaled.
func (h Header) MarshalSize() int {
// NOTE: Be careful to match the MarshalTo() method.
// Flags (1 byte)
size := 1
// Authentication Length (1 byte)
size += 1
// Message Identifier Hash (2 bytes)
size += 2
// Authentication Data (variable)
// Should be the same as int(h.AuthenticationLength) * 4
size += len(h.AuthenticationData) * 4
// Originating Source
if h.AddressType == 0 {
size += 4
} else {
size += 16
}
if h.PayloadType == "" {
// payloadType is omitted
return size
}
size += len([]byte(h.PayloadType))
// Trailing zero after the Payload Type
size++
return size
}
// Unmarshal parses the passed byte slice and stores the result in the Header.
func (h *Header) Unmarshal(buf []byte) error {
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| V=1 |A|R|T|E|C| auth len | msg id hash |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
: originating source (32 or 128 bits) :
: :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| optional authentication data |
: .... :
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
| optional payload type |
+ +-+- - - - - - - - - -+
| |0| payload |
+ - - - - - - - - - - - - - - - - - - - - +-+- - - - - - - - - -|
*/
currentPosition := 0
if len(buf[currentPosition:]) < 1 {
return errBufTooSmallForFlags
}
// The first two bits are always zero
// The third bit contains the Version Number
h.Version = (buf[currentPosition] >> versionShift) & oneBitMask
// The fourth bit is the address type bit
h.AddressType = AddressType(buf[currentPosition]>>addressShift) & oneBitMask
// The fifth bit is the reserved bit
h.Reserved = (buf[currentPosition] >> reservedShift) & oneBitMask
// The sixth bit is the message type bit
h.MessageType = MessageType(buf[currentPosition]>>messageTypeShift) & oneBitMask
// The seventh bit is the encrypted bit
h.Encrypted = (buf[currentPosition] >> encryptedShift) & oneBitMask
// The last bit is the compressed bit
h.Compressed = (buf[currentPosition] >> compressedShift) & oneBitMask
// First Byte
currentPosition++
// Authentication Length
if len(buf[currentPosition:]) < 1 {
return errBufTooSmallForAuthLength
}
h.AuthenticationLength = buf[currentPosition]
currentPosition++
// Message Id Hash
if len(buf[currentPosition:]) < 2 {
return errBufTooSmallForMsgIdHash
}
h.MessageIDHash = binary.BigEndian.Uint16(buf[currentPosition : currentPosition+2])
currentPosition += 2
// Originating Source
switch h.AddressType {
case 0: // Expecting IPv4
if len(buf[currentPosition:]) < 4 {
return errBufTooSmallForIPv4
}
h.OriginatingSource = net.IPv4(buf[currentPosition], buf[currentPosition+1], buf[currentPosition+2], buf[currentPosition+3])
currentPosition += 4
case 1: // Expecting IPv6
if len(buf[currentPosition:]) < 16 {
return errBufTooSmallForIPv6
}
h.OriginatingSource = net.IP(buf[currentPosition : currentPosition+16])
currentPosition += 16
}
// Authentication Data
if h.AuthenticationLength != 0 {
if len(buf[currentPosition:]) < int(h.AuthenticationLength)*4 {
return errBufTooSmallForAuthData
}
h.AuthenticationData = make([]uint32, h.AuthenticationLength)
for i := 0; i < int(h.AuthenticationLength); i++ {
h.AuthenticationData[i] = binary.BigEndian.Uint32(buf[currentPosition : currentPosition+4])
currentPosition += 4 // each uint32 take up 4 bytes
}
}
if len(buf[currentPosition:]) < 3 || (len(buf[currentPosition:]) >= 3 && string(buf[currentPosition:currentPosition+3]) == "v=0") {
// whether there's is no payload or the payload type has been omitted
// and we are already in the payload
h.PayloadType = ""
} else {
// either there is a payload type in the header
// or the payload type is "application/sdp" (implicit because it's omitted)
// and the payload itself is not SDP (because it doesn't start with "v=0")
i := 0
for ; i < len(buf[currentPosition:]); i++ {
if buf[currentPosition+i] == 0 { // looking for the trailing zero byte
break
}
}
if i == len(buf[currentPosition:]) && buf[currentPosition+(i-1)] != 0 {
// we traversed the whole buffer but didnt find a trailing byte
return errNoTrailingByteFound
}
mediaType, _, err := mime.ParseMediaType(string(buf[currentPosition : currentPosition+i])) // doesn't include the trailing zero
if err != nil {
// the string until the trailing zero is not a valid mime media type
// this indicates the payload type has been omitted (thus being application/sdp) and we are already in the payload
// since we already checked and the start of the payload is not "v=0", the payload is not of type "application/sdp"
return err
}
// Payload Type
h.PayloadType = mediaType
}
return nil
}
// Marshal serializes the header into bytes.
func (h Header) Marshal() (buf []byte, err error) {
buf = make([]byte, h.MarshalSize())
n, err := h.MarshalTo(buf)
if err != nil {
return nil, err
}
return buf[:n], nil
}
// MarshalTo serializes the header and writes to the buffer.
// It returns the number of bytes read n and any error.
func (h Header) MarshalTo(buf []byte) (n int, err error) {
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| V=1 |A|R|T|E|C| auth len | msg id hash |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
: originating source (32 or 128 bits) :
: :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| optional authentication data |
: .... :
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
| optional payload type |
+ +-+- - - - - - - - - -+
| |0| payload |
+ - - - - - - - - - - - - - - - - - - - - +-+- - - - - - - - - -|
*/
size := h.MarshalSize()
if size > len(buf) {
return 0, io.ErrShortBuffer
}
// This is the number of bytes marshalled
currentPosition := 0
// The first two bits are always zero
// The third bit contains the Version Number
buf[currentPosition] = (h.Version << versionShift)
// The fourth bit is the address type bit
buf[currentPosition] |= byte((h.AddressType << addressShift))
// The fifth bit is the reserved bit
buf[currentPosition] |= (h.Reserved << reservedShift)
// The sixth bit is the message type bit
buf[currentPosition] |= byte((h.MessageType << messageTypeShift))
// The seventh bit is the encrypted bit
buf[currentPosition] |= (h.Encrypted << encryptedShift)
// The last bit is the compressed bit
buf[currentPosition] |= (h.Compressed << compressedShift)
// First byte
currentPosition++
// Authentication Length
buf[currentPosition] = h.AuthenticationLength
currentPosition++
// Message Id Hash
binary.BigEndian.PutUint16(buf[currentPosition:], h.MessageIDHash)
currentPosition += 2
// Originating Source
if h.OriginatingSource.To4() != nil {
copy(buf[currentPosition:], h.OriginatingSource.To4())
currentPosition += 4
} else if h.OriginatingSource.To16() != nil {
copy(buf[currentPosition:], h.OriginatingSource.To16())
currentPosition += 16
} else {
return 0, errInvalidIPOnHeader
}
// Authentication Data
for _, data := range h.AuthenticationData {
binary.BigEndian.PutUint32(buf[currentPosition:currentPosition+4], data)
currentPosition += 4 // each uint32 take up 4 bytes
}
// Payload Type
if h.PayloadType != "" {
payloadTypeBytes := []byte(h.PayloadType)
for i := range payloadTypeBytes {
buf[currentPosition+i] = payloadTypeBytes[i]
}
currentPosition += len(payloadTypeBytes)
// Trailing zero after the Payload Type
buf[currentPosition] = 0
currentPosition++
}
// PayloadType has been omitted
// which indicates it is "application/sdp"
return currentPosition, nil
}
// Clone returns a deep copy of h.
func (h Header) Clone() Header {
clone := h
// Deep copy of AuthenticationData slice
if h.AuthenticationData != nil {
clone.AuthenticationData = make([]uint32, len(h.AuthenticationData))
copy(clone.AuthenticationData, h.AuthenticationData)
}
// Deep copy of OriginatingSource IP (net.IP is a slice internally)
if h.OriginatingSource != nil {
clone.OriginatingSource = make(net.IP, len(h.OriginatingSource))
copy(clone.OriginatingSource, h.OriginatingSource)
}
// Copy string
clone.PayloadType = h.PayloadType
return clone
}