44package sctp
55
66import (
7+ "encoding/binary"
78 "testing"
89
910 "github.com/stretchr/testify/assert"
@@ -60,7 +61,7 @@ func TestParamReconfigResponse_Failure(t *testing.T) {
6061 }
6162}
6263
63- func TestReconfigResultStringer (t * testing.T ) {
64+ func TestReconfigResultString (t * testing.T ) {
6465 tt := []struct {
6566 result reconfigResult
6667 expected string
@@ -79,3 +80,223 @@ func TestReconfigResultStringer(t *testing.T) {
7980 assert .Equalf (t , tc .expected , actual , "Test case %d" , i )
8081 }
8182}
83+
84+ func TestParamReconfigResponse_OptionalFields (t * testing.T ) {
85+ type tc struct {
86+ name string
87+ raw []byte
88+ wantRSN uint32
89+ wantResult reconfigResult
90+ wantSenderPresent bool
91+ wantSender uint32
92+ wantReceiverPresent bool
93+ wantReceiver uint32
94+ wantParamHeaderLen int
95+ }
96+
97+ // build a parameter with optional fields.
98+ build := func (rsn uint32 , res reconfigResult , sender * uint32 , receiver * uint32 ) []byte {
99+ // +4 if sender and +4 if receiver)
100+ valLen := 8
101+ if sender != nil {
102+ valLen += 4
103+ }
104+
105+ if receiver != nil {
106+ valLen += 4
107+ }
108+
109+ total := 4 + valLen // header + value
110+ b := make ([]byte , total )
111+
112+ // header
113+ binary .BigEndian .PutUint16 (b [0 :], uint16 (reconfigResp ))
114+ binary .BigEndian .PutUint16 (b [2 :], uint16 (total )) //nolint:gosec
115+
116+ // value
117+ binary .BigEndian .PutUint32 (b [4 :], rsn )
118+ binary .BigEndian .PutUint32 (b [8 :], uint32 (res ))
119+
120+ off := 12
121+ if sender != nil {
122+ binary .BigEndian .PutUint32 (b [off :], * sender )
123+ off += 4
124+ }
125+
126+ if receiver != nil {
127+ binary .BigEndian .PutUint32 (b [off :], * receiver )
128+ }
129+
130+ return b
131+ }
132+
133+ senderOnly := uint32 (0x01020304 )
134+ bothSender := uint32 (0xA1B2C3D4 )
135+ bothReceiver := uint32 (0x0A0B0C0D )
136+
137+ tests := []tc {
138+ {
139+ name : "sender_next_only" ,
140+ raw : build (0x0000002A , reconfigResultErrorWrongSSN , & senderOnly , nil ),
141+ wantRSN : 0x0000002A ,
142+ wantResult : reconfigResultErrorWrongSSN ,
143+ wantSenderPresent : true ,
144+ wantSender : senderOnly ,
145+ wantReceiverPresent : false ,
146+ wantParamHeaderLen : 16 , // 4 (hdr) + 12 (value)
147+ },
148+ {
149+ name : "sender_and_receiver_next" ,
150+ raw : build (0x00000007 , reconfigResultSuccessPerformed , & bothSender , & bothReceiver ),
151+ wantRSN : 0x00000007 ,
152+ wantResult : reconfigResultSuccessPerformed ,
153+ wantSenderPresent : true ,
154+ wantSender : bothSender ,
155+ wantReceiverPresent : true ,
156+ wantReceiver : bothReceiver ,
157+ wantParamHeaderLen : 20 , // 4 (hdr) + 16 (value)
158+ },
159+ }
160+
161+ for _ , tt := range tests {
162+ t .Run (tt .name , func (t * testing.T ) {
163+ var param paramReconfigResponse
164+
165+ _ , err := param .unmarshal (tt .raw )
166+ assert .NoError (t , err )
167+
168+ assert .Equal (t , reconfigResp , param .paramHeader .typ )
169+ assert .Equal (t , tt .wantParamHeaderLen , param .paramHeader .len )
170+ // p.paramHeader.raw should equal just the value part
171+ assert .Equal (t , tt .raw [4 :], param .paramHeader .raw )
172+
173+ assert .Equal (t , tt .wantRSN , param .reconfigResponseSequenceNumber )
174+ assert .Equal (t , tt .wantResult , param .result )
175+ assert .Equal (t , tt .wantSenderPresent , param .senderNextTSNPresent )
176+
177+ if tt .wantSenderPresent {
178+ assert .Equal (t , tt .wantSender , param .senderNextTSN )
179+ }
180+
181+ assert .Equal (t , tt .wantReceiverPresent , param .receiverNextTSNPresent )
182+
183+ if tt .wantReceiverPresent {
184+ assert .Equal (t , tt .wantReceiver , param .receiverNextTSN )
185+ }
186+
187+ out , err := param .marshal ()
188+ assert .NoError (t , err )
189+ assert .Equal (t , tt .raw , out )
190+ })
191+ }
192+ }
193+
194+ func TestParamReconfigResponse_OptionalFields_Roundtrip (t * testing.T ) {
195+ tcs := []struct {
196+ name string
197+ input paramReconfigResponse
198+ valLen int // value length excluding the 4-byte header (8/12/16)
199+ }{
200+ {
201+ name : "NoOptionals" ,
202+ input : paramReconfigResponse {
203+ reconfigResponseSequenceNumber : 0x0A ,
204+ result : reconfigResultSuccessPerformed ,
205+ },
206+ valLen : 8 ,
207+ },
208+ {
209+ name : "SenderOnly" ,
210+ input : paramReconfigResponse {
211+ reconfigResponseSequenceNumber : 0x0B ,
212+ result : reconfigResultDenied ,
213+ senderNextTSNPresent : true ,
214+ senderNextTSN : 0x12345678 ,
215+ },
216+ valLen : 12 ,
217+ },
218+ {
219+ name : "SenderAndReceiver" ,
220+ input : paramReconfigResponse {
221+ reconfigResponseSequenceNumber : 0x0C ,
222+ result : reconfigResultInProgress ,
223+ senderNextTSNPresent : true ,
224+ senderNextTSN : 0x11111111 ,
225+ receiverNextTSNPresent : true ,
226+ receiverNextTSN : 0x22222222 ,
227+ },
228+ valLen : 16 ,
229+ },
230+ }
231+
232+ for _ , tc := range tcs {
233+ t .Run (tc .name , func (t * testing.T ) {
234+ b , err := tc .input .marshal ()
235+ assert .NoError (t , err )
236+ assert .GreaterOrEqual (t , len (b ), 4 )
237+
238+ pt := binary .BigEndian .Uint16 (b [0 :2 ])
239+ pl := binary .BigEndian .Uint16 (b [2 :4 ])
240+
241+ assert .Equal (t , uint16 (reconfigResp ), pt )
242+ assert .Equal (t , uint16 (4 + tc .valLen ), pl ) //nolint:gosec
243+ assert .Equal (t , int (pl ), len (b ))
244+
245+ var out paramReconfigResponse
246+ p , err := out .unmarshal (b )
247+
248+ assert .NoError (t , err )
249+ got , ok := p .(* paramReconfigResponse )
250+ assert .True (t , ok , "expected *paramReconfigResponse, got %T" , p )
251+
252+ assert .Equal (t , tc .input .reconfigResponseSequenceNumber , got .reconfigResponseSequenceNumber )
253+ assert .Equal (t , tc .input .result , got .result )
254+ assert .Equal (t , tc .input .senderNextTSNPresent , got .senderNextTSNPresent )
255+ assert .Equal (t , tc .input .senderNextTSN , got .senderNextTSN )
256+ assert .Equal (t , tc .input .receiverNextTSNPresent , got .receiverNextTSNPresent )
257+ assert .Equal (t , tc .input .receiverNextTSN , got .receiverNextTSN )
258+
259+ b2 , err := got .marshal ()
260+ assert .NoError (t , err )
261+ assert .Equal (t , b , b2 )
262+ })
263+ }
264+ }
265+
266+ func TestParamReconfigResponse_Marshal_OrderingGuard (t * testing.T ) {
267+ // receiver present without sender present must fail
268+ r := paramReconfigResponse {
269+ reconfigResponseSequenceNumber : 1 ,
270+ result : reconfigResultSuccessPerformed ,
271+ receiverNextTSNPresent : true ,
272+ receiverNextTSN : 7 ,
273+ }
274+
275+ _ , err := r .marshal ()
276+ assert .Error (t , err )
277+ assert .ErrorIs (t , err , ErrReconfigRespParamInvalidCombo )
278+ }
279+
280+ func TestParamReconfigResponse_Unmarshal_InvalidLengths (t * testing.T ) {
281+ // too short
282+ badShort := make ([]byte , 10 )
283+ binary .BigEndian .PutUint16 (badShort [0 :], uint16 (reconfigResp ))
284+ binary .BigEndian .PutUint16 (badShort [2 :], uint16 (10 ))
285+
286+ var a paramReconfigResponse
287+ _ , err := a .unmarshal (badShort )
288+
289+ assert .Error (t , err )
290+ assert .ErrorIs (t , err , ErrReconfigRespParamTooShort )
291+
292+ // invalid value length
293+ badLen := make ([]byte , 14 )
294+ binary .BigEndian .PutUint16 (badLen [0 :], uint16 (reconfigResp ))
295+ binary .BigEndian .PutUint16 (badLen [2 :], uint16 (14 ))
296+
297+ var b paramReconfigResponse
298+ _ , err = b .unmarshal (badLen )
299+
300+ assert .Error (t , err )
301+ assert .ErrorIs (t , err , ErrReconfigRespParamInvalidLength )
302+ }
0 commit comments