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"
50 static bool stun_agent_is_unknown (StunAgent
*agent
, uint16_t type
);
51 static unsigned stun_agent_find_unknowns (StunAgent
*agent
,
52 const StunMessage
* msg
, uint16_t *list
, unsigned max
);
54 void stun_agent_init (StunAgent
*agent
, const uint16_t *known_attributes
,
55 StunCompatibility compatibility
, StunAgentUsageFlags usage_flags
)
59 agent
->known_attributes
= (uint16_t *) known_attributes
;
60 agent
->compatibility
= compatibility
;
61 agent
->usage_flags
= usage_flags
;
63 for (i
= 0; i
< STUN_AGENT_MAX_SAVED_IDS
; i
++) {
64 agent
->sent_ids
[i
].valid
= FALSE
;
69 bool stun_agent_default_validater (StunAgent
*agent
,
70 StunMessage
*message
, uint8_t *username
, uint16_t username_len
,
71 uint8_t **password
, size_t *password_len
, void *user_data
)
73 StunDefaultValidaterData
* val
= (StunDefaultValidaterData
*) user_data
;
76 for (i
= 0; val
&& val
[i
].username
; i
++) {
77 stun_debug ("Comparing username '");
78 stun_debug_bytes (username
, username_len
);
79 stun_debug ("' (%d) with '", username_len
);
80 stun_debug_bytes (val
[i
].username
, val
[i
].username_len
);
81 stun_debug ("' (%d) : %d\n",
82 val
[i
].username_len
, memcmp (username
, val
[i
].username
, username_len
));
83 if (username_len
== val
[i
].username_len
&&
84 memcmp (username
, val
[i
].username
, username_len
) == 0) {
85 *password
= (uint8_t *) val
[i
].password
;
86 *password_len
= val
[i
].password_len
;
87 stun_debug ("Found valid username, returning password : '%s'\n", *password
);
96 StunValidationStatus
stun_agent_validate (StunAgent
*agent
, StunMessage
*msg
,
97 const uint8_t *buffer
, size_t buffer_len
,
98 StunMessageIntegrityValidate validater
, void * validater_data
)
100 StunTransactionId msg_id
;
104 uint8_t *username
= NULL
;
105 uint16_t username_len
;
111 int sent_id_idx
= -1;
114 int ignore_credentials
= 0;
115 uint8_t long_term_key
[16];
116 bool long_term_key_valid
= FALSE
;
118 len
= stun_message_validate_buffer_length (buffer
, buffer_len
);
119 if (len
== STUN_MESSAGE_BUFFER_INVALID
) {
120 return STUN_VALIDATION_NOT_STUN
;
121 } else if (len
== STUN_MESSAGE_BUFFER_INCOMPLETE
) {
122 return STUN_VALIDATION_INCOMPLETE_STUN
;
123 } else if (len
!= (int) buffer_len
) {
124 return STUN_VALIDATION_NOT_STUN
;
127 msg
->buffer
= (uint8_t *) buffer
;
128 msg
->buffer_len
= buffer_len
;
132 msg
->long_term_valid
= FALSE
;
134 /* TODO: reject it or not ? */
135 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
136 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
137 !stun_message_has_cookie (msg
)) {
138 stun_debug ("STUN demux error: no cookie!\n");
139 return STUN_VALIDATION_BAD_REQUEST
;
142 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
143 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
144 agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
) {
145 /* Looks for FINGERPRINT */
146 if (stun_message_find32 (msg
, STUN_ATTRIBUTE_FINGERPRINT
, &fpr
) !=
147 STUN_MESSAGE_RETURN_SUCCESS
) {
148 stun_debug ("STUN demux error: no FINGERPRINT attribute!\n");
149 return STUN_VALIDATION_BAD_REQUEST
;
151 /* Checks FINGERPRINT */
152 crc32
= stun_fingerprint (msg
->buffer
, stun_message_length (msg
),
153 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
);
156 stun_debug ("STUN demux error: bad fingerprint: 0x%08x,"
157 " expected: 0x%08x!\n", fpr
, crc32
);
158 return STUN_VALIDATION_BAD_REQUEST
;
161 stun_debug ("STUN demux: OK!\n");
164 if (stun_message_get_class (msg
) == STUN_RESPONSE
||
165 stun_message_get_class (msg
) == STUN_ERROR
) {
166 stun_message_id (msg
, msg_id
);
167 for (sent_id_idx
= 0; sent_id_idx
< STUN_AGENT_MAX_SAVED_IDS
; sent_id_idx
++) {
168 if (agent
->sent_ids
[sent_id_idx
].valid
== TRUE
&&
169 agent
->sent_ids
[sent_id_idx
].method
== stun_message_get_method (msg
) &&
170 memcmp (msg_id
, agent
->sent_ids
[sent_id_idx
].id
,
171 sizeof(StunTransactionId
)) == 0) {
173 key
= agent
->sent_ids
[sent_id_idx
].key
;
174 key_len
= agent
->sent_ids
[sent_id_idx
].key_len
;
175 memcpy (long_term_key
, agent
->sent_ids
[sent_id_idx
].long_term_key
,
176 sizeof(long_term_key
));
177 long_term_key_valid
= agent
->sent_ids
[sent_id_idx
].long_term_valid
;
181 if (sent_id_idx
== STUN_AGENT_MAX_SAVED_IDS
) {
182 return STUN_VALIDATION_UNMATCHED_RESPONSE
;
187 (agent
->usage_flags
& STUN_AGENT_USAGE_IGNORE_CREDENTIALS
) ||
188 (stun_message_get_class (msg
) == STUN_ERROR
&&
189 stun_message_find_error (msg
, &error_code
) ==
190 STUN_MESSAGE_RETURN_SUCCESS
&&
191 (error_code
== 400 || error_code
== 401)) ||
192 (stun_message_get_class (msg
) == STUN_INDICATION
&&
193 (agent
->usage_flags
& STUN_AGENT_USAGE_NO_INDICATION_AUTH
));
196 ignore_credentials
== 0 &&
197 (stun_message_get_class (msg
) == STUN_REQUEST
||
198 stun_message_get_class (msg
) == STUN_INDICATION
) &&
199 (((agent
->usage_flags
& STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS
) &&
200 (!stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) ||
201 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
))) ||
202 ((agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) &&
203 stun_message_get_class (msg
) == STUN_REQUEST
&&
204 (!stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) ||
205 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
) ||
206 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_NONCE
) ||
207 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_REALM
))) ||
208 ((agent
->usage_flags
& STUN_AGENT_USAGE_IGNORE_CREDENTIALS
) == 0 &&
209 stun_message_has_attribute (msg
, STUN_ATTRIBUTE_USERNAME
) &&
210 !stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
)))) {
211 return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST
;
214 if (stun_message_has_attribute (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
) &&
215 ((key
== NULL
&& ignore_credentials
== 0) ||
216 (agent
->usage_flags
& STUN_AGENT_USAGE_FORCE_VALIDATER
))) {
218 username
= (uint8_t *) stun_message_find (msg
, STUN_ATTRIBUTE_USERNAME
,
220 if (validater
== NULL
||
221 validater (agent
, msg
, username
, username_len
,
222 &key
, &key_len
, validater_data
) == FALSE
) {
223 return STUN_VALIDATION_UNAUTHORIZED
;
227 if (ignore_credentials
== 0 && key
!= NULL
&& key_len
> 0) {
228 hash
= (uint8_t *) stun_message_find (msg
,
229 STUN_ATTRIBUTE_MESSAGE_INTEGRITY
, &hlen
);
232 /* We must give the size from start to the end of the attribute
233 because you might have a FINGERPRINT attribute after it... */
234 if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
235 uint8_t *realm
= NULL
;
236 uint8_t *username
= NULL
;
238 uint16_t username_len
;
241 if (long_term_key_valid
) {
242 memcpy (md5
, long_term_key
, sizeof (md5
));
244 realm
= (uint8_t *) stun_message_find (msg
, STUN_ATTRIBUTE_REALM
, &realm_len
);
245 username
= (uint8_t *) stun_message_find (msg
,
246 STUN_ATTRIBUTE_USERNAME
, &username_len
);
247 if (username
== NULL
|| realm
== NULL
) {
248 return STUN_VALIDATION_UNAUTHORIZED
;
250 stun_hash_creds (realm
, realm_len
,
251 username
, username_len
,
255 memcpy (msg
->long_term_key
, md5
, sizeof(md5
));
256 msg
->long_term_valid
= TRUE
;
258 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
259 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
, hash
- msg
->buffer
,
260 sha
, md5
, sizeof(md5
), TRUE
);
261 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
262 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
263 stun_message_length (msg
) - 20, sha
, md5
, sizeof(md5
), TRUE
);
265 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
266 hash
- msg
->buffer
, sha
, md5
, sizeof(md5
), FALSE
);
269 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
270 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
, hash
- msg
->buffer
,
271 sha
, key
, key_len
, TRUE
);
272 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
273 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
274 stun_message_length (msg
) - 20, sha
, key
, key_len
, TRUE
);
276 stun_sha1 (msg
->buffer
, hash
+ 20 - msg
->buffer
,
277 hash
- msg
->buffer
, sha
, key
, key_len
, FALSE
);
281 stun_debug (" Message HMAC-SHA1 fingerprint:");
282 stun_debug ("\nkey : ");
283 stun_debug_bytes (key
, key_len
);
284 stun_debug ("\n expected: ");
285 stun_debug_bytes (sha
, sizeof (sha
));
286 stun_debug ("\n received: ");
287 stun_debug_bytes (hash
, sizeof (sha
));
290 if (memcmp (sha
, hash
, sizeof (sha
))) {
291 stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n");
292 return STUN_VALIDATION_UNAUTHORIZED
;
295 stun_debug ("STUN auth: OK!\n");
297 msg
->key_len
= key_len
;
298 } else if (!(stun_message_get_class (msg
) == STUN_ERROR
&&
299 stun_message_find_error (msg
, &error_code
) ==
300 STUN_MESSAGE_RETURN_SUCCESS
&&
301 (error_code
== 400 || error_code
== 401))) {
302 stun_debug ("STUN auth error: No message integrity attribute!\n");
303 return STUN_VALIDATION_UNAUTHORIZED
;
308 if (sent_id_idx
!= -1 && sent_id_idx
< STUN_AGENT_MAX_SAVED_IDS
) {
309 agent
->sent_ids
[sent_id_idx
].valid
= FALSE
;
312 if (stun_agent_find_unknowns (agent
, msg
, &unknown
, 1) > 0) {
313 if (stun_message_get_class (msg
) == STUN_REQUEST
)
314 return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE
;
316 return STUN_VALIDATION_UNKNOWN_ATTRIBUTE
;
318 return STUN_VALIDATION_SUCCESS
;
323 bool stun_agent_init_request (StunAgent
*agent
, StunMessage
*msg
,
324 uint8_t *buffer
, size_t buffer_len
, StunMethod m
)
327 StunTransactionId id
;
329 msg
->buffer
= buffer
;
330 msg
->buffer_len
= buffer_len
;
334 msg
->long_term_valid
= FALSE
;
336 stun_make_transid (id
);
338 ret
= stun_message_init (msg
, STUN_REQUEST
, m
, id
);
341 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
342 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
343 uint32_t cookie
= htonl (STUN_MAGIC_COOKIE
);
344 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, &cookie
, sizeof (cookie
));
352 bool stun_agent_init_indication (StunAgent
*agent
, StunMessage
*msg
,
353 uint8_t *buffer
, size_t buffer_len
, StunMethod m
)
356 StunTransactionId id
;
358 msg
->buffer
= buffer
;
359 msg
->buffer_len
= buffer_len
;
363 msg
->long_term_valid
= FALSE
;
365 stun_make_transid (id
);
366 ret
= stun_message_init (msg
, STUN_INDICATION
, m
, id
);
369 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
370 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
371 uint32_t cookie
= htonl (STUN_MAGIC_COOKIE
);
372 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, &cookie
, sizeof (cookie
));
380 bool stun_agent_init_response (StunAgent
*agent
, StunMessage
*msg
,
381 uint8_t *buffer
, size_t buffer_len
, const StunMessage
*request
)
384 StunTransactionId id
;
386 if (stun_message_get_class (request
) != STUN_REQUEST
) {
390 msg
->buffer
= buffer
;
391 msg
->buffer_len
= buffer_len
;
393 msg
->key
= request
->key
;
394 msg
->key_len
= request
->key_len
;
395 memmove (msg
->long_term_key
, request
->long_term_key
,
396 sizeof(msg
->long_term_key
));
397 msg
->long_term_valid
= request
->long_term_valid
;
399 stun_message_id (request
, id
);
401 if (stun_message_init (msg
, STUN_RESPONSE
,
402 stun_message_get_method (request
), id
)) {
404 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
405 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
406 agent
->usage_flags
& STUN_AGENT_USAGE_ADD_SOFTWARE
) {
407 stun_message_append_software (msg
);
415 bool stun_agent_init_error (StunAgent
*agent
, StunMessage
*msg
,
416 uint8_t *buffer
, size_t buffer_len
, const StunMessage
*request
,
419 StunTransactionId id
;
421 if (stun_message_get_class (request
) != STUN_REQUEST
) {
425 msg
->buffer
= buffer
;
426 msg
->buffer_len
= buffer_len
;
428 msg
->key
= request
->key
;
429 msg
->key_len
= request
->key_len
;
430 memmove (msg
->long_term_key
, request
->long_term_key
,
431 sizeof(msg
->long_term_key
));
432 msg
->long_term_valid
= request
->long_term_valid
;
434 stun_message_id (request
, id
);
437 if (stun_message_init (msg
, STUN_ERROR
,
438 stun_message_get_method (request
), id
)) {
440 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
441 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
442 agent
->usage_flags
& STUN_AGENT_USAGE_ADD_SOFTWARE
) {
443 stun_message_append_software (msg
);
445 if (stun_message_append_error (msg
, err
) == STUN_MESSAGE_RETURN_SUCCESS
) {
453 size_t stun_agent_build_unknown_attributes_error (StunAgent
*agent
,
454 StunMessage
*msg
, uint8_t *buffer
, size_t buffer_len
,
455 const StunMessage
*request
)
459 uint16_t ids
[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES
];
461 counter
= stun_agent_find_unknowns (agent
, request
,
462 ids
, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES
);
464 if (stun_agent_init_error (agent
, msg
, buffer
, buffer_len
,
465 request
, STUN_ERROR_UNKNOWN_ATTRIBUTE
) == FALSE
) {
469 /* NOTE: Old RFC3489 compatibility:
470 * When counter is odd, duplicate one value for 32-bits padding. */
471 if (!stun_message_has_cookie (request
) && (counter
& 1))
472 ids
[counter
++] = ids
[0];
474 if (stun_message_append_bytes (msg
, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES
,
475 ids
, counter
* 2) == STUN_MESSAGE_RETURN_SUCCESS
) {
476 return stun_agent_finish_message (agent
, msg
, request
->key
, request
->key_len
);
483 size_t stun_agent_finish_message (StunAgent
*agent
, StunMessage
*msg
,
484 const uint8_t *key
, size_t key_len
)
489 StunTransactionId id
;
492 if (msg
->key
!= NULL
) {
494 key_len
= msg
->key_len
;
500 if (msg
->long_term_valid
) {
501 memcpy (md5
, msg
->long_term_key
, sizeof(msg
->long_term_key
));
502 } else if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
503 uint8_t *realm
= NULL
;
504 uint8_t *username
= NULL
;
506 uint16_t username_len
;
508 realm
= (uint8_t *) stun_message_find (msg
,
509 STUN_ATTRIBUTE_REALM
, &realm_len
);
510 username
= (uint8_t *) stun_message_find (msg
,
511 STUN_ATTRIBUTE_USERNAME
, &username_len
);
512 if (username
== NULL
|| realm
== NULL
) {
515 stun_hash_creds (realm
, realm_len
,
516 username
, username_len
,
519 memcpy (msg
->long_term_key
, md5
, sizeof(msg
->long_term_key
));
520 msg
->long_term_valid
= TRUE
;
523 /* If no realm/username and long term credentials,
524 then don't send the message integrity */
526 ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_MESSAGE_INTEGRITY
, 20);
530 if (agent
->usage_flags
& STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS
) {
531 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
532 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
533 stun_message_length (msg
) - 20, ptr
, md5
, sizeof(md5
), TRUE
);
534 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
536 if (agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
)
539 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
540 stun_message_length (msg
) - minus
, ptr
, md5
, sizeof(md5
), TRUE
);
542 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
543 stun_message_length (msg
) - 20, ptr
, md5
, sizeof(md5
), FALSE
);
546 if (agent
->compatibility
== STUN_COMPATIBILITY_RFC3489
) {
547 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
548 stun_message_length (msg
) - 20, ptr
, key
, key_len
, TRUE
);
549 } else if (agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) {
551 if (agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
)
554 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
555 stun_message_length (msg
) - minus
, ptr
, key
, key_len
, TRUE
);
557 stun_sha1 (msg
->buffer
, stun_message_length (msg
),
558 stun_message_length (msg
) - 20, ptr
, key
, key_len
, FALSE
);
562 stun_debug (" Message HMAC-SHA1 message integrity:"
564 stun_debug_bytes (key
, key_len
);
565 stun_debug ("\n sent : ");
566 stun_debug_bytes (ptr
, 20);
571 if ((agent
->compatibility
== STUN_COMPATIBILITY_RFC5389
||
572 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
) &&
573 agent
->usage_flags
& STUN_AGENT_USAGE_USE_FINGERPRINT
) {
574 ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_FINGERPRINT
, 4);
579 fpr
= stun_fingerprint (msg
->buffer
, stun_message_length (msg
),
580 agent
->compatibility
== STUN_COMPATIBILITY_WLM2009
);
581 memcpy (ptr
, &fpr
, sizeof (fpr
));
583 stun_debug (" Message HMAC-SHA1 fingerprint: ");
584 stun_debug_bytes (ptr
, 4);
589 if (stun_message_get_class (msg
) == STUN_REQUEST
) {
590 for (i
= 0; i
< STUN_AGENT_MAX_SAVED_IDS
; i
++) {
591 if (agent
->sent_ids
[i
].valid
== FALSE
) {
592 stun_message_id (msg
, id
);
593 memcpy (agent
->sent_ids
[i
].id
, id
, sizeof(StunTransactionId
));
594 agent
->sent_ids
[i
].method
= stun_message_get_method (msg
);
595 agent
->sent_ids
[i
].key
= (uint8_t *) key
;
596 agent
->sent_ids
[i
].key_len
= key_len
;
597 memcpy (agent
->sent_ids
[i
].long_term_key
, msg
->long_term_key
,
598 sizeof(msg
->long_term_key
));
599 agent
->sent_ids
[i
].long_term_valid
= msg
->long_term_valid
;
600 agent
->sent_ids
[i
].valid
= TRUE
;
606 msg
->key
= (uint8_t *) key
;
607 msg
->key_len
= key_len
;
608 return stun_message_length (msg
);
612 static bool stun_agent_is_unknown (StunAgent
*agent
, uint16_t type
)
615 uint16_t *known_attr
= agent
->known_attributes
;
617 while(*known_attr
!= 0) {
618 if (*known_attr
== type
) {
630 stun_agent_find_unknowns (StunAgent
*agent
, const StunMessage
* msg
,
631 uint16_t *list
, unsigned max
)
634 uint16_t len
= stun_message_length (msg
);
637 offset
= STUN_MESSAGE_ATTRIBUTES_POS
;
639 while ((offset
< len
) && (count
< max
))
641 size_t alen
= stun_getw (msg
->buffer
+ offset
+ STUN_ATTRIBUTE_TYPE_LEN
);
642 uint16_t atype
= stun_getw (msg
->buffer
+ offset
);
644 offset
+= STUN_ATTRIBUTE_VALUE_POS
+ stun_align (alen
);
646 if (!stun_optional (atype
) && stun_agent_is_unknown (agent
, atype
))
648 stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)\n",
649 (unsigned)atype
, (unsigned)alen
);
650 list
[count
++] = htons (atype
);
654 stun_debug ("STUN unknown: %u mandatory attribute(s)!\n", count
);