-
Notifications
You must be signed in to change notification settings - Fork 79
/
chunkheader.go
100 lines (85 loc) · 3.31 KB
/
chunkheader.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
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package sctp
import (
"encoding/binary"
"errors"
"fmt"
)
/*
chunkHeader represents a SCTP Chunk header, defined in https://tools.ietf.org/html/rfc4960#section-3.2
The figure below illustrates the field format for the chunks to be
transmitted in the SCTP packet. Each chunk is formatted with a Chunk
Type field, a chunk-specific Flag field, a Chunk Length field, and a
Value field.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Chunk Type | Chunk Flags | Chunk Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Chunk Value |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type chunkHeader struct {
typ chunkType
flags byte
raw []byte
}
const (
chunkHeaderSize = 4
)
// SCTP chunk header errors
var (
ErrChunkHeaderTooSmall = errors.New("raw is too small for a SCTP chunk")
ErrChunkHeaderNotEnoughSpace = errors.New("not enough data left in SCTP packet to satisfy requested length")
ErrChunkHeaderPaddingNonZero = errors.New("chunk padding is non-zero at offset")
)
func (c *chunkHeader) unmarshal(raw []byte) error {
if len(raw) < chunkHeaderSize {
return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrChunkHeaderTooSmall, len(raw), chunkHeaderSize)
}
c.typ = chunkType(raw[0])
c.flags = raw[1]
length := binary.BigEndian.Uint16(raw[2:])
// Length includes Chunk header
valueLength := int(length - chunkHeaderSize)
lengthAfterValue := len(raw) - (chunkHeaderSize + valueLength)
if lengthAfterValue < 0 {
return fmt.Errorf("%w: remain %d req %d ", ErrChunkHeaderNotEnoughSpace, valueLength, len(raw)-chunkHeaderSize)
} else if lengthAfterValue < 4 {
// https://tools.ietf.org/html/rfc4960#section-3.2
// The Chunk Length field does not count any chunk padding.
// Chunks (including Type, Length, and Value fields) are padded out
// by the sender with all zero bytes to be a multiple of 4 bytes
// long. This padding MUST NOT be more than 3 bytes in total. The
// Chunk Length value does not include terminating padding of the
// chunk. However, it does include padding of any variable-length
// parameter except the last parameter in the chunk. The receiver
// MUST ignore the padding.
for i := lengthAfterValue; i > 0; i-- {
paddingOffset := chunkHeaderSize + valueLength + (i - 1)
if raw[paddingOffset] != 0 {
return fmt.Errorf("%w: %d ", ErrChunkHeaderPaddingNonZero, paddingOffset)
}
}
}
c.raw = raw[chunkHeaderSize : chunkHeaderSize+valueLength]
return nil
}
func (c *chunkHeader) marshal() ([]byte, error) {
raw := make([]byte, 4+len(c.raw))
raw[0] = uint8(c.typ)
raw[1] = c.flags
binary.BigEndian.PutUint16(raw[2:], uint16(len(c.raw)+chunkHeaderSize))
copy(raw[4:], c.raw)
return raw, nil
}
func (c *chunkHeader) valueLength() int {
return len(c.raw)
}
// String makes chunkHeader printable
func (c chunkHeader) String() string {
return c.typ.String()
}