@@ -10,20 +10,31 @@ import (
1010)
1111
1212/*
13- chunkHeader represents a SCTP Chunk header, defined in https://tools.ietf.org/html/rfc4960#section-3.2
14- The figure below illustrates the field format for the chunks to be
15- transmitted in the SCTP packet. Each chunk is formatted with a Chunk
16- Type field, a chunk-specific Flag field, a Chunk Length field, and a
17- Value field.
13+ chunkHeader represents an SCTP Chunk header per RFC 9260 section 3.2.
14+
15+ Each chunk is formatted with:
16+ - 1 byte Chunk Type
17+ - 1 byte Chunk Flags
18+ - 2 bytes Chunk Length (includes header + value, excludes trailing padding)
19+
20+ The sender MUST pad the chunk to a 4-byte boundary with zero bytes (up to 3).
21+ The receiver MUST ignore padding.
22+
23+ Chunk header layout:
1824
1925 0 1 2 3
2026 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
2127 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22- | Chunk Type | Chunk Flags | Chunk Length |
28+ | Chunk Type | Chunk Flags | Chunk Length |
2329 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24- | |
25- | Chunk Value |
26- | |
30+
31+ Chunk value (follows header; variable length, may be followed by up to 3 bytes of zero padding):
32+
33+ 0 1 2 3
34+ 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
35+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36+ | Chunk Value |
37+ | ... |
2738 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2839*/
2940type chunkHeader struct {
4152 ErrChunkHeaderTooSmall = errors .New ("raw is too small for a SCTP chunk" )
4253 ErrChunkHeaderNotEnoughSpace = errors .New ("not enough data left in SCTP packet to satisfy requested length" )
4354 ErrChunkHeaderPaddingNonZero = errors .New ("chunk padding is non-zero at offset" )
55+ ErrChunkHeaderInvalidLength = errors .New ("chunk length field smaller than header length" )
4456)
4557
46- func (c * chunkHeader ) unmarshal (raw []byte ) error {
58+ func (c * chunkHeader ) unmarshal (raw []byte ) error { //nolint:cyclop
4759 if len (raw ) < chunkHeaderSize {
4860 return fmt .Errorf (
4961 "%w: raw only %d bytes, %d is the minimum length" ,
@@ -55,42 +67,42 @@ func (c *chunkHeader) unmarshal(raw []byte) error {
5567 c .flags = raw [1 ]
5668 length := binary .BigEndian .Uint16 (raw [2 :])
5769
58- // Length includes Chunk header
59- valueLength := int (length - chunkHeaderSize )
60- lengthAfterValue := len (raw ) - (chunkHeaderSize + valueLength )
61-
62- if lengthAfterValue < 0 {
63- return fmt .Errorf ("%w: remain %d req %d " , ErrChunkHeaderNotEnoughSpace , valueLength , len (raw )- chunkHeaderSize )
64- } else if lengthAfterValue < 4 {
65- // https://tools.ietf.org/html/rfc4960#section-3.2
66- // The Chunk Length field does not count any chunk padding.
67- // Chunks (including Type, Length, and Value fields) are padded out
68- // by the sender with all zero bytes to be a multiple of 4 bytes
69- // long. This padding MUST NOT be more than 3 bytes in total. The
70- // Chunk Length value does not include terminating padding of the
71- // chunk. However, it does include padding of any variable-length
72- // parameter except the last parameter in the chunk. The receiver
73- // MUST ignore the padding.
74- for i := lengthAfterValue ; i > 0 ; i -- {
75- paddingOffset := chunkHeaderSize + valueLength + (i - 1 )
76- if raw [paddingOffset ] != 0 {
77- return fmt .Errorf ("%w: %d " , ErrChunkHeaderPaddingNonZero , paddingOffset )
78- }
79- }
70+ // RFC 9260 section 3.2: Chunk Length MUST be >= 4.
71+ if length < chunkHeaderSize {
72+ return fmt .Errorf ("%w: length=%d" , ErrChunkHeaderInvalidLength , length )
73+ }
74+
75+ // Ensure we have the full (header+value) bytes available in this slice.
76+ if int (length ) > len (raw ) {
77+ return fmt .Errorf ("%w: need %d bytes, have %d" , ErrChunkHeaderNotEnoughSpace , length , len (raw ))
8078 }
8179
80+ valueLength := int (length ) - chunkHeaderSize
8281 c .raw = raw [chunkHeaderSize : chunkHeaderSize + valueLength ]
8382
83+ // Sender pads to a 4-byte boundary with zeros, receiver MUST ignore padding.
84+ // If padding bytes are present in this slice, validate they are zero.
85+ pad := int ((4 - (length & 0x3 )) & 0x3 ) // 0..3 bytes
86+ remain := len (raw ) - int (length )
87+
88+ if pad > 0 && remain > 0 {
89+ for i := 0 ; i < min (remain , pad ); i ++ {
90+ if raw [int (length )+ i ] != 0 {
91+ return fmt .Errorf ("%w: %d" , ErrChunkHeaderPaddingNonZero , int (length )+ i )
92+ }
93+ }
94+ }
95+
8496 return nil
8597}
8698
8799func (c * chunkHeader ) marshal () ([]byte , error ) {
88- raw := make ([]byte , 4 + len (c .raw ))
100+ raw := make ([]byte , chunkHeaderSize + len (c .raw ))
89101
90102 raw [0 ] = uint8 (c .typ )
91103 raw [1 ] = c .flags
92104 binary .BigEndian .PutUint16 (raw [2 :], uint16 (len (c .raw )+ chunkHeaderSize )) //nolint:gosec // G115
93- copy (raw [4 :], c .raw )
105+ copy (raw [chunkHeaderSize :], c .raw )
94106
95107 return raw , nil
96108}
0 commit comments