2 * This file is part of the Nice GLib ICE library.
4 * (C) 2007 Nokia Corporation. All rights reserved.
5 * Contact: Rémi Denis-Courmont
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is the Nice GLib ICE library.
19 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
20 * Corporation. All Rights Reserved.
23 * Rémi Denis-Courmont, Nokia
25 * Alternatively, the contents of this file may be used under the terms of the
26 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
27 * case the provisions of LGPL are applicable instead of those above. If you
28 * wish to allow use of your version of this file only under the terms of the
29 * LGPL and not to allow others to use your version of this file under the
30 * MPL, indicate your decision by deleting the provisions above and replace
31 * them with the notice and other provisions required by the LGPL. If you do
32 * not delete the provisions above, a recipient may use your version of this
33 * file under either the MPL or the LGPL.
40 #include "stunmessage.h"
41 #include "stunagent.h"
47 static bool stun_agent_is_unknown (StunAgent
*agent
, uint16_t type
);
48 static unsigned stun_agent_find_unknowns (StunAgent
*agent
,
49 const StunMessage
* msg
, uint16_t *list
, unsigned max
);
51 void stun_agent_init (StunAgent
*agent
, const uint16_t *known_attributes
,
52 StunCompatibility compatibility
, uint32_t usage_flags
)
56 agent
->known_attributes
= (uint16_t *) known_attributes
;
57 agent
->compatibility
= compatibility
;
58 agent
->usage_flags
= usage_flags
;
60 for (i
= 0; i
< STUN_AGENT_MAX_SAVED_IDS
; i
++) {
61 agent
->sent_ids
[i
].valid
= FALSE
;
66 bool stun_agent_default_validater (StunAgent
*agent
,
67 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
68 uint8_t **password
, size_t *password_len
, void *user_data
)
70 stun_validater_data
* val
= (stun_validater_data
*) user_data
;
73 for (i
= 0; val
&& val
[i
].username
; i
++) {
74 stun_debug ("Comparing username '");
75 stun_debug_bytes (username
, username_len
);
76 stun_debug ("' (%d) with '", username_len
);
77 stun_debug_bytes (val
[i
].username
, val
[i
].username_len
);
78 stun_debug ("' (%d) : %d\n",
79 val
[i
].username_len
, memcmp (username
, val
[i
].username
, username_len
));
80 if (username_len
== val
[i
].username_len
&&
81 memcmp (username
, val
[i
].username
, username_len
) == 0) {
82 *password
= (uint8_t *) val
[i
].password
;
83 *password_len
= val
[i
].password_len
;
84 stun_debug ("Found valid username, returning password : '%s'\n", *password
);
93 StunValidationStatus
stun_agent_validate (StunAgent
*agent
, StunMessage
*msg
,
94 const uint8_t *buffer
, size_t buffer_len
,
95 StunMessageIntegrityValidate validater
, void * validater_data
)
97 stun_transid_t msg_id
;
101 uint8_t *username
= NULL
;
102 uint16_t username_len
;
108 int sent_id_idx
= -1;
111 int ignore_credentials
= 0;
112 uint8_t long_term_key
[16];
113 bool long_term_key_valid
= FALSE
;
115 len
= stun_message_validate_buffer_length (buffer
, buffer_len
);
116 if (len
== STUN_MESSAGE_BUFFER_INVALID
) {
117 return STUN_VALIDATION_NOT_STUN
;
118 } else if (len
== STUN_MESSAGE_BUFFER_INCOMPLETE
) {
119 return STUN_VALIDATION_INCOMPLETE_STUN
;
120 } else if (len
!= (int) buffer_len
) {
121 return STUN_VALIDATION_NOT_STUN
;
124 msg
->buffer
= (uint8_t *) buffer
;
125 msg
->buffer_len
= buffer_len
;
129 msg
->long_term_valid
= FALSE
;
131 /* TODO: reject it or not ? */
132 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
133 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
134 !stun_has_cookie (msg
)) {
135 stun_debug ("STUN demux error: no cookie!\n");
136 return STUN_VALIDATION_BAD_REQUEST
;
139 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
140 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
141 agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
) {
142 /* Looks for FINGERPRINT */
143 if (stun_message_find32 (msg
, STUN_ATTRIBUTE_FINGERPRINT
, &fpr
) !=
144 STUN_MESSAGE_RETURN_SUCCESS
) {
145 stun_debug ("STUN demux error: no FINGERPRINT attribute!\n");
146 return STUN_VALIDATION_BAD_REQUEST
;
149 /* Checks FINGERPRINT */
150 crc32
= stun_fingerprint (msg
->buffer
, stun_message_length (msg
));
153 stun_debug ("STUN demux error: bad fingerprint: 0x%08x,"
154 " expected: 0x%08x!\n", fpr
, crc32
);
155 if (agent
->compatibility
!= STUN_COMPATIBILITY_WLM2009
)
156 return STUN_VALIDATION_BAD_REQUEST
;
159 stun_debug ("STUN demux: OK!\n");
162 if (stun_message_get_class (msg
) == STUN_RESPONSE
||
163 stun_message_get_class (msg
) == STUN_ERROR
) {
164 stun_message_id (msg
, msg_id
);
165 for (sent_id_idx
= 0; sent_id_idx
< STUN_AGENT_MAX_SAVED_IDS
; sent_id_idx
++) {
166 if (agent
->sent_ids
[sent_id_idx
].valid
== TRUE
&&
167 agent
->sent_ids
[sent_id_idx
].method
== stun_message_get_method (msg
) &&
168 memcmp (msg_id
, agent
->sent_ids
[sent_id_idx
].id
,
169 sizeof(stun_transid_t
)) == 0) {
171 key
= agent
->sent_ids
[sent_id_idx
].key
;
172 key_len
= agent
->sent_ids
[sent_id_idx
].key_len
;
173 memcpy (long_term_key
, agent
->sent_ids
[sent_id_idx
].long_term_key
,
174 sizeof(long_term_key
));
175 long_term_key_valid
= agent
->sent_ids
[sent_id_idx
].long_term_valid
;
179 if (sent_id_idx
== STUN_AGENT_MAX_SAVED_IDS
) {
180 return STUN_VALIDATION_UNMATCHED_RESPONSE
;
185 (agent
->usage_flags
& STUN_AGENT_USAGE_IGNORE_CREDENTIALS
) ||
186 (stun_message_get_class (msg
) == STUN_ERROR
&&
187 stun_message_find_error (msg
, &error_code
) ==
188 STUN_MESSAGE_RETURN_SUCCESS
&&
189 (error_code
== 400 || error_code
== 401)) ||
190 (stun_message_get_class (msg
) == STUN_INDICATION
&&
191 (agent
->usage_flags
& STUN_AGENT_USAGE_NO_INDICATION_AUTH
));
194 ignore_credentials
== 0 &&
195 (stun_message_get_class (msg
) == STUN_REQUEST
||
196 stun_message_get_class (msg
) == STUN_INDICATION
) &&
197 (((agent
->usage_flags
& STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS
) &&
198 (!stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) ||
199 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
))) ||
200 ((agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) &&
201 stun_message_get_class (msg
) == STUN_REQUEST
&&
202 (!stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) ||
203 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
) ||
204 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_NONCE
) ||
205 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_REALM
))) ||
206 ((agent
->usage_flags
& STUN_AGENT_USAGE_IGNORE_CREDENTIALS
) == 0 &&
207 stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) &&
208 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
)))) {
209 return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
;
212 if (stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
) &&
213 ((key
== NULL
&& ignore_credentials
== 0) ||
214 (agent
->usage_flags
& STUN_AGENT_USAGE_FORCE_VALIDATER
))) {
216 username
= (uint8_t *) stun_message_find (msg
, STUN_ATTRIBUTE_USERNAME
,
218 if (validater
== NULL
||
219 validater (agent
, msg
, username
, username_len
,
220 &key
, &key_len
, validater_data
) == FALSE
) {
221 return STUN_VALIDATION_UNAUTHORIZED
;
225 if (ignore_credentials
== 0 && key
!= NULL
&& key_len
> 0) {
226 hash
= (uint8_t *) stun_message_find (msg
,
227 STUN_ATTRIBUTE_MESSAGE_INTEGRITY
, &hlen
);
230 /* We must give the size from start to the end of the attribute
231 because you might have a FINGERPRINT attribute after it... */
232 if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
233 uint8_t *realm
= NULL
;
234 uint8_t *username
= NULL
;
236 uint16_t username_len
;
239 if (long_term_key_valid
) {
240 memcpy (md5
, long_term_key
, sizeof (md5
));
242 realm
= (uint8_t *) stun_message_find (msg
, STUN_ATTRIBUTE_REALM
, &realm_len
);
243 username
= (uint8_t *) stun_message_find (msg
,
244 STUN_ATTRIBUTE_USERNAME
, &username_len
);
245 if (username
== NULL
|| realm
== NULL
) {
246 return STUN_VALIDATION_UNAUTHORIZED
;
248 stun_hash_creds (realm
, realm_len
,
249 username
, username_len
,
253 memcpy (msg
->long_term_key
, md5
, sizeof(md5
));
254 msg
->long_term_valid
= TRUE
;
256 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
257 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
, hash
- msg
->buffer
,
258 sha
, md5
, sizeof(md5
), TRUE
);
259 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
260 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
261 stun_message_length (msg
) - 20, sha
, md5
, sizeof(md5
), TRUE
);
263 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
264 hash
- msg
->buffer
, sha
, md5
, sizeof(md5
), FALSE
);
267 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
268 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
, hash
- msg
->buffer
,
269 sha
, key
, key_len
, TRUE
);
270 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
271 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
272 stun_message_length (msg
) - 20, sha
, key
, key_len
, TRUE
);
274 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
275 hash
- msg
->buffer
, sha
, key
, key_len
, FALSE
);
279 stun_debug (" Message HMAC-SHA1 fingerprint:");
280 stun_debug ("\nkey : ");
281 stun_debug_bytes (key
, key_len
);
282 stun_debug ("\n expected: ");
283 stun_debug_bytes (sha
, sizeof (sha
));
284 stun_debug ("\n received: ");
285 stun_debug_bytes (hash
, sizeof (sha
));
288 if (memcmp (sha
, hash
, sizeof (sha
))) {
289 stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n");
290 return STUN_VALIDATION_UNAUTHORIZED
;
293 stun_debug ("STUN auth: OK!\n");
295 msg
->key_len
= key_len
;
296 } else if (!(stun_message_get_class (msg
) == STUN_ERROR
&&
297 stun_message_find_error (msg
, &error_code
) ==
298 STUN_MESSAGE_RETURN_SUCCESS
&&
299 (error_code
== 400 || error_code
== 401))) {
300 stun_debug ("STUN auth error: No message integrity attribute!\n");
301 return STUN_VALIDATION_UNAUTHORIZED
;
306 if (sent_id_idx
!= -1 && sent_id_idx
< STUN_AGENT_MAX_SAVED_IDS
) {
307 agent
->sent_ids
[sent_id_idx
].valid
= FALSE
;
310 if (stun_agent_find_unknowns (agent
, msg
, &unknown
, 1) > 0) {
311 if (stun_message_get_class (msg
) == STUN_REQUEST
)
312 return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
;
314 return STUN_VALIDATION_UNKNOWN_ATTRIBUTE
;
316 return STUN_VALIDATION_SUCCESS
;
321 bool stun_agent_init_request (StunAgent
*agent
, StunMessage
*msg
,
322 uint8_t *buffer
, size_t buffer_len
, stun_method_t m
)
327 msg
->buffer
= buffer
;
328 msg
->buffer_len
= buffer_len
;
332 msg
->long_term_valid
= FALSE
;
334 stun_make_transid (id
);
336 ret
= stun_message_init (msg
, STUN_REQUEST
, m
, id
);
339 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
340 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
341 uint32_t cookie
= htonl (STUN_MAGIC_COOKIE
);
342 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, &cookie
, sizeof (cookie
));
350 bool stun_agent_init_indication (StunAgent
*agent
, StunMessage
*msg
,
351 uint8_t *buffer
, size_t buffer_len
, stun_method_t m
)
356 msg
->buffer
= buffer
;
357 msg
->buffer_len
= buffer_len
;
361 msg
->long_term_valid
= FALSE
;
363 stun_make_transid (id
);
364 ret
= stun_message_init (msg
, STUN_INDICATION
, m
, id
);
367 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
368 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
369 uint32_t cookie
= htonl (STUN_MAGIC_COOKIE
);
370 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, &cookie
, sizeof (cookie
));
378 bool stun_agent_init_response (StunAgent
*agent
, StunMessage
*msg
,
379 uint8_t *buffer
, size_t buffer_len
, const StunMessage
*request
)
384 if (stun_message_get_class (request
) != STUN_REQUEST
) {
388 msg
->buffer
= buffer
;
389 msg
->buffer_len
= buffer_len
;
391 msg
->key
= request
->key
;
392 msg
->key_len
= request
->key_len
;
393 memmove (msg
->long_term_key
, request
->long_term_key
,
394 sizeof(msg
->long_term_key
));
395 msg
->long_term_valid
= request
->long_term_valid
;
397 stun_message_id (request
, id
);
399 if (stun_message_init (msg
, STUN_RESPONSE
,
400 stun_message_get_method (request
), id
)) {
402 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
403 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
404 agent
->usage_flags
& STUN_AGENT_USAGE_ADD_SOFTWARE
) {
405 stun_message_append_software (msg
);
413 bool stun_agent_init_error (StunAgent
*agent
, StunMessage
*msg
,
414 uint8_t *buffer
, size_t buffer_len
, const StunMessage
*request
,
419 if (stun_message_get_class (request
) != STUN_REQUEST
) {
423 msg
->buffer
= buffer
;
424 msg
->buffer_len
= buffer_len
;
426 msg
->key
= request
->key
;
427 msg
->key_len
= request
->key_len
;
428 memmove (msg
->long_term_key
, request
->long_term_key
,
429 sizeof(msg
->long_term_key
));
430 msg
->long_term_valid
= request
->long_term_valid
;
432 stun_message_id (request
, id
);
435 if (stun_message_init (msg
, STUN_ERROR
,
436 stun_message_get_method (request
), id
)) {
438 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
439 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
440 agent
->usage_flags
& STUN_AGENT_USAGE_ADD_SOFTWARE
) {
441 stun_message_append_software (msg
);
443 if (stun_message_append_error (msg
, err
) == STUN_MESSAGE_RETURN_SUCCESS
) {
451 size_t stun_agent_build_unknown_attributes_error (StunAgent
*agent
,
452 StunMessage
*msg
, uint8_t *buffer
, size_t buffer_len
,
453 const StunMessage
*request
)
457 uint16_t ids
[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES
];
459 counter
= stun_agent_find_unknowns (agent
, request
,
460 ids
, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES
);
462 if (stun_agent_init_error (agent
, msg
, buffer
, buffer_len
,
463 request
, STUN_ERROR_UNKNOWN_ATTRIBUTE
) == FALSE
) {
467 /* NOTE: Old RFC3489 compatibility:
468 * When counter is odd, duplicate one value for 32-bits padding. */
469 if (!stun_has_cookie (request
) && (counter
& 1))
470 ids
[counter
++] = ids
[0];
472 if (stun_message_append_bytes (msg
, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES
,
473 ids
, counter
* 2) == STUN_MESSAGE_RETURN_SUCCESS
) {
474 return stun_agent_finish_message (agent
, msg
, request
->key
, request
->key_len
);
481 size_t stun_agent_finish_message (StunAgent
*agent
, StunMessage
*msg
,
482 const uint8_t *key
, size_t key_len
)
490 if (msg
->key
!= NULL
) {
492 key_len
= msg
->key_len
;
498 if (msg
->long_term_valid
) {
499 memcpy (md5
, msg
->long_term_key
, sizeof(msg
->long_term_key
));
500 } else if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
501 uint8_t *realm
= NULL
;
502 uint8_t *username
= NULL
;
504 uint16_t username_len
;
506 realm
= (uint8_t *) stun_message_find (msg
,
507 STUN_ATTRIBUTE_REALM
, &realm_len
);
508 username
= (uint8_t *) stun_message_find (msg
,
509 STUN_ATTRIBUTE_USERNAME
, &username_len
);
510 if (username
== NULL
|| realm
== NULL
) {
513 stun_hash_creds (realm
, realm_len
,
514 username
, username_len
,
517 memcpy (msg
->long_term_key
, md5
, sizeof(msg
->long_term_key
));
518 msg
->long_term_valid
= TRUE
;
521 /* If no realm/username and long term credentials,
522 then don't send the message integrity */
524 ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
, 20);
528 if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
529 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
530 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
531 stun_message_length (msg
) - 20, ptr
, md5
, sizeof(md5
), TRUE
);
532 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
534 if (agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
)
537 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
538 stun_message_length (msg
) - minus
, ptr
, md5
, sizeof(md5
), TRUE
);
540 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
541 stun_message_length (msg
) - 20, ptr
, md5
, sizeof(md5
), FALSE
);
544 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
545 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
546 stun_message_length (msg
) - 20, ptr
, key
, key_len
, TRUE
);
547 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
549 if (agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
)
552 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
553 stun_message_length (msg
) - minus
, ptr
, key
, key_len
, TRUE
);
555 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
556 stun_message_length (msg
) - 20, ptr
, key
, key_len
, FALSE
);
560 stun_debug (" Message HMAC-SHA1 message integrity:"
562 stun_debug_bytes (key
, key_len
);
563 stun_debug ("\n sent : ");
564 stun_debug_bytes (ptr
, 20);
569 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
570 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
571 agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
) {
572 ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_FINGERPRINT
, 4);
578 fpr
= stun_fingerprint (msg
->buffer
, stun_message_length (msg
));
579 memcpy (ptr
, &fpr
, sizeof (fpr
));
581 stun_debug (" Message HMAC-SHA1 fingerprint: ");
582 stun_debug_bytes (ptr
, 4);
587 if (stun_message_get_class (msg
) == STUN_REQUEST
) {
588 for (i
= 0; i
< STUN_AGENT_MAX_SAVED_IDS
; i
++) {
589 if (agent
->sent_ids
[i
].valid
== FALSE
) {
590 stun_message_id (msg
, id
);
591 memcpy (agent
->sent_ids
[i
].id
, id
, sizeof(stun_transid_t
));
592 agent
->sent_ids
[i
].method
= stun_message_get_method (msg
);
593 agent
->sent_ids
[i
].key
= (uint8_t *) key
;
594 agent
->sent_ids
[i
].key_len
= key_len
;
595 memcpy (agent
->sent_ids
[i
].long_term_key
, msg
->long_term_key
,
596 sizeof(msg
->long_term_key
));
597 agent
->sent_ids
[i
].long_term_valid
= msg
->long_term_valid
;
598 agent
->sent_ids
[i
].valid
= TRUE
;
604 msg
->key
= (uint8_t *) key
;
605 msg
->key_len
= key_len
;
606 return stun_message_length (msg
);
610 static bool stun_agent_is_unknown (StunAgent
*agent
, uint16_t type
)
613 uint16_t *known_attr
= agent
->known_attributes
;
615 while(*known_attr
!= 0) {
616 if (*known_attr
== type
) {
628 stun_agent_find_unknowns (StunAgent
*agent
, const StunMessage
* msg
,
629 uint16_t *list
, unsigned max
)
632 uint16_t len
= stun_message_length (msg
);
635 offset
= STUN_MESSAGE_ATTRIBUTES_POS
;
637 while ((offset
< len
) && (count
< max
))
639 size_t alen
= stun_getw (msg
->buffer
+ offset
+ STUN_ATTRIBUTE_TYPE_LEN
);
640 uint16_t atype
= stun_getw (msg
->buffer
+ offset
);
642 offset
+= STUN_ATTRIBUTE_VALUE_POS
+ stun_align (alen
);
644 if (!stun_optional (atype
) && stun_agent_is_unknown (agent
, atype
))
646 stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)\n",
647 (unsigned)atype
, (unsigned)alen
);
648 list
[count
++] = htons (atype
);
652 stun_debug ("STUN unknown: %u mandatory attribute(s)!\n", count
);