forked from pion/webrtc
/
chunk.go
157 lines (138 loc) · 4.42 KB
/
chunk.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
package sctp
import (
"encoding/binary"
"fmt"
"github.com/pkg/errors"
)
// chunkType is an enum for SCTP Chunk Type field
// This field identifies the type of information contained in the
// Chunk Value field.
type chunkType uint8
// List of known chunkType enums
const (
PAYLOADDATA chunkType = 0
INIT chunkType = 1
INITACK chunkType = 2
SACK chunkType = 3
HEARTBEAT chunkType = 4
HEARTBEATACK chunkType = 5
ABORT chunkType = 6
SHUTDOWN chunkType = 7
SHUTDOWNACK chunkType = 8
ERROR chunkType = 9
COOKIEECHO chunkType = 10
COOKIEACK chunkType = 11
CWR chunkType = 13
SHUTDOWNCOMPLETE chunkType = 14
)
func (c chunkType) String() string {
switch c {
case PAYLOADDATA:
return "Payload data"
case INIT:
return "Initiation"
case INITACK:
return "Initiation Acknowledgement"
case SACK:
return "Selective Acknowledgement"
case HEARTBEAT:
return "Heartbeat"
case HEARTBEATACK:
return "Heartbeat Acknowledgement"
case ABORT:
return "Abort"
case SHUTDOWN:
return "Shutdown"
case SHUTDOWNACK:
return "Shutdown Acknowledgement"
case ERROR:
return "Error"
case COOKIEECHO:
return "Cookie Echo"
case COOKIEACK:
return "Cookie Acknowledgement"
case CWR:
return "Congestion Window Reduced"
case SHUTDOWNCOMPLETE:
return "Shutdown Complete"
default:
return fmt.Sprintf("Unknown ChunkType: %d", c)
}
}
/*
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
)
func (c *chunkHeader) unmarshal(raw []byte) error {
if len(raw) < chunkHeaderSize {
return errors.Errorf("raw only %d bytes, %d is the minimum length for a SCTP chunk", 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 errors.Errorf("Not enough data left in SCTP packet to satisfy requested length remain %d req %d ", 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 errors.Errorf("Chunk padding is non-zero at offset %d ", 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)
}
type chunk interface {
unmarshal(raw []byte) error
marshal() ([]byte, error)
check() (bool, error)
valueLength() int
}
// String makes chunkHeader printable
func (c chunkHeader) String() string {
return c.typ.String()
}