2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2008-2009 Nokia Corporation. All rights reserved.
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
24 * Youness Alaoui, Collabora Ltd.
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
41 #include "stunmessage.h"
42 #include "stunagent.h"
52 static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type);
53 static unsigned stun_agent_find_unknowns (StunAgent *agent,
54 const StunMessage * msg, uint16_t *list, unsigned max);
56 void stun_agent_init (StunAgent *agent, const uint16_t *known_attributes,
57 StunCompatibility compatibility, StunAgentUsageFlags usage_flags)
61 agent->known_attributes = (uint16_t *) known_attributes;
62 agent->compatibility = compatibility;
63 agent->usage_flags = usage_flags;
64 agent->software_attribute = NULL;
65 agent->ms_ice2_send_legacy_connchecks =
66 compatibility == STUN_COMPATIBILITY_MSICE2;
68 for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
69 agent->sent_ids[i].valid = FALSE;
74 bool stun_agent_default_validater (StunAgent *agent,
75 StunMessage *message, uint8_t *username, uint16_t username_len,
76 uint8_t **password, size_t *password_len, void *user_data)
78 StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data;
81 for (i = 0; val && val[i].username ; i++) {
83 stun_debug ("Comparing username of size %d and %" PRIuPTR ": %d",
84 username_len, val[i].username_len,
85 (memcmp (username, val[i].username, username_len) == 0));
87 stun_debug_bytes (" First username: ", username, username_len);
88 stun_debug_bytes (" Second username: ", val[i].username,
90 if (username_len == val[i].username_len &&
91 memcmp (username, val[i].username, username_len) == 0) {
92 *password = (uint8_t *) val[i].password;
93 *password_len = val[i].password_len;
94 stun_debug ("Found valid username, returning password : '%s'", *password);
103 static bool stun_agent_check_fingerprint(StunAgent *agent, StunMessage *msg)
109 /* Looks for FINGERPRINT */
110 if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) !=
111 STUN_MESSAGE_RETURN_SUCCESS) {
112 stun_debug ("STUN demux error: no FINGERPRINT attribute!");
116 msg_len = stun_message_length (msg);
118 /* Checks FINGERPRINT */
119 crc32 = stun_fingerprint (msg->buffer, msg_len, FALSE);
124 /* [MS-ICE2] 3.1.4.8.2 Connectivity Checks Phase - legacy compatibility */
125 if (agent->compatibility == STUN_COMPATIBILITY_MSICE2 &&
126 stun_message_find (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
128 fpr == stun_fingerprint (msg->buffer, msg_len, TRUE)) {
132 stun_debug ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!",
140 StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
141 const uint8_t *buffer, size_t buffer_len,
142 StunMessageIntegrityValidate validater, void * validater_data)
144 StunTransactionId msg_id;
146 uint8_t *username = NULL;
147 uint16_t username_len;
153 uint32_t implementation_version;
154 int sent_id_idx = -1;
157 int ignore_credentials = 0;
158 uint8_t long_term_key[16] = { 0 };
159 bool long_term_key_valid = FALSE;
161 len = stun_message_validate_buffer_length (buffer, buffer_len,
162 !(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES));
163 if (len == STUN_MESSAGE_BUFFER_INVALID) {
164 return STUN_VALIDATION_NOT_STUN;
165 } else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) {
166 return STUN_VALIDATION_INCOMPLETE_STUN;
167 } else if (len != (int) buffer_len) {
168 return STUN_VALIDATION_NOT_STUN;
171 msg->buffer = (uint8_t *) buffer;
172 msg->buffer_len = buffer_len;
176 msg->long_term_valid = FALSE;
178 /* TODO: reject it or not ? */
179 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
180 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
181 !stun_message_has_cookie (msg)) {
182 stun_debug ("STUN demux error: no cookie!");
183 return STUN_VALIDATION_BAD_REQUEST;
186 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
187 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
188 agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
189 if (stun_agent_check_fingerprint(agent, msg) == FALSE) {
190 return STUN_VALIDATION_BAD_REQUEST;
193 stun_debug ("STUN demux: OK!");
196 if (stun_message_get_class (msg) == STUN_RESPONSE ||
197 stun_message_get_class (msg) == STUN_ERROR) {
198 stun_message_id (msg, msg_id);
199 for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) {
200 if (agent->sent_ids[sent_id_idx].valid == TRUE &&
201 agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) &&
202 memcmp (msg_id, agent->sent_ids[sent_id_idx].id,
203 sizeof(StunTransactionId)) == 0) {
205 key = agent->sent_ids[sent_id_idx].key;
206 key_len = agent->sent_ids[sent_id_idx].key_len;
207 memcpy (long_term_key, agent->sent_ids[sent_id_idx].long_term_key,
208 sizeof(long_term_key));
209 long_term_key_valid = agent->sent_ids[sent_id_idx].long_term_valid;
213 if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
214 return STUN_VALIDATION_UNMATCHED_RESPONSE;
219 (agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) ||
220 (stun_message_get_class (msg) == STUN_ERROR &&
221 stun_message_find_error (msg, &error_code) ==
222 STUN_MESSAGE_RETURN_SUCCESS &&
223 (error_code == STUN_ERROR_BAD_REQUEST ||
224 error_code == STUN_ERROR_UNAUTHORIZED ||
225 error_code == STUN_ERROR_STALE_NONCE ||
226 error_code == STUN_ERROR_TRY_ALTERNATE)) ||
227 (stun_message_get_class (msg) == STUN_INDICATION &&
228 (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS ||
229 agent->usage_flags & STUN_AGENT_USAGE_NO_INDICATION_AUTH));
232 ignore_credentials == 0 &&
233 (stun_message_get_class (msg) == STUN_REQUEST ||
234 stun_message_get_class (msg) == STUN_INDICATION) &&
235 (((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS) &&
236 (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
237 !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) ||
238 ((agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) &&
239 stun_message_get_class (msg) == STUN_REQUEST &&
240 (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) ||
241 !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) ||
242 !stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) ||
243 !stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) ||
244 ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 &&
245 stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) &&
246 !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY)))) {
247 return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST;
250 if (stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) &&
251 ((key == NULL && ignore_credentials == 0) ||
252 (agent->usage_flags & STUN_AGENT_USAGE_FORCE_VALIDATER))) {
254 username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME,
256 if (validater == NULL ||
257 validater (agent, msg, username, username_len,
258 &key, &key_len, validater_data) == FALSE) {
259 return STUN_VALIDATION_UNAUTHORIZED;
263 if (ignore_credentials == 0 && key != NULL && key_len > 0) {
264 hash = (uint8_t *) stun_message_find (msg,
265 STUN_ATTRIBUTE_MESSAGE_INTEGRITY, &hlen);
268 /* We must give the size from start to the end of the attribute
269 because you might have a FINGERPRINT attribute after it... */
270 if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
271 uint8_t *realm = NULL;
275 if (long_term_key_valid) {
276 memcpy (md5, long_term_key, sizeof (md5));
278 realm = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_REALM, &realm_len);
279 username = (uint8_t *) stun_message_find (msg,
280 STUN_ATTRIBUTE_USERNAME, &username_len);
281 if (username == NULL || realm == NULL) {
282 return STUN_VALIDATION_UNAUTHORIZED;
284 stun_hash_creds (realm, realm_len,
285 username, username_len,
289 memcpy (msg->long_term_key, md5, sizeof(md5));
290 msg->long_term_valid = TRUE;
292 if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
293 agent->compatibility == STUN_COMPATIBILITY_OC2007) {
294 stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
295 sha, md5, sizeof(md5), TRUE);
296 } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
297 stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
298 stun_message_length (msg) - 20, sha, md5, sizeof(md5), TRUE);
300 stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
301 hash - msg->buffer, sha, md5, sizeof(md5), FALSE);
304 if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
305 agent->compatibility == STUN_COMPATIBILITY_OC2007) {
306 stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
307 sha, key, key_len, TRUE);
308 } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
309 stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
310 stun_message_length (msg) - 20, sha, key, key_len, TRUE);
312 stun_sha1 (msg->buffer, hash + 20 - msg->buffer,
313 hash - msg->buffer, sha, key, key_len, FALSE);
317 stun_debug (" Message HMAC-SHA1 fingerprint:");
318 stun_debug_bytes (" key : ", key, key_len);
319 stun_debug_bytes (" expected: ", sha, sizeof (sha));
320 stun_debug_bytes (" received: ", hash, sizeof (sha));
322 if (memcmp (sha, hash, sizeof (sha))) {
323 stun_debug ("STUN auth error: SHA1 fingerprint mismatch!");
324 return STUN_VALIDATION_UNAUTHORIZED;
327 stun_debug ("STUN auth: OK!");
329 msg->key_len = key_len;
330 } else if (!(stun_message_get_class (msg) == STUN_ERROR &&
331 stun_message_find_error (msg, &error_code) ==
332 STUN_MESSAGE_RETURN_SUCCESS &&
333 (error_code == STUN_ERROR_BAD_REQUEST ||
334 error_code == STUN_ERROR_UNAUTHORIZED))) {
335 stun_debug ("STUN auth error: No message integrity attribute!");
336 return STUN_VALIDATION_UNAUTHORIZED;
341 if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
342 agent->sent_ids[sent_id_idx].valid = FALSE;
345 /* [MS-ICE2] 3.1.4.8.2 stop sending additional connectivity checks */
346 if (stun_message_find32(msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
347 &implementation_version) == STUN_MESSAGE_RETURN_SUCCESS) {
348 msg->agent->ms_ice2_send_legacy_connchecks = FALSE;
351 if (stun_agent_find_unknowns (agent, msg, &unknown, 1) > 0) {
352 if (stun_message_get_class (msg) == STUN_REQUEST)
353 return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE;
355 return STUN_VALIDATION_UNKNOWN_ATTRIBUTE;
357 return STUN_VALIDATION_SUCCESS;
361 bool stun_agent_forget_transaction (StunAgent *agent, StunTransactionId id)
365 for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
366 if (agent->sent_ids[i].valid == TRUE &&
367 memcmp (id, agent->sent_ids[i].id,
368 sizeof(StunTransactionId)) == 0) {
369 agent->sent_ids[i].valid = FALSE;
377 bool stun_agent_init_request (StunAgent *agent, StunMessage *msg,
378 uint8_t *buffer, size_t buffer_len, StunMethod m)
381 StunTransactionId id;
383 msg->buffer = buffer;
384 msg->buffer_len = buffer_len;
388 msg->long_term_valid = FALSE;
390 stun_make_transid (id);
392 ret = stun_message_init (msg, STUN_REQUEST, m, id);
395 if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
396 agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
397 uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
398 memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
400 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
401 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
402 (agent->software_attribute != NULL ||
403 agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
404 stun_message_append_software (msg, agent->software_attribute);
412 bool stun_agent_init_indication (StunAgent *agent, StunMessage *msg,
413 uint8_t *buffer, size_t buffer_len, StunMethod m)
416 StunTransactionId id;
418 msg->buffer = buffer;
419 msg->buffer_len = buffer_len;
423 msg->long_term_valid = FALSE;
425 stun_make_transid (id);
426 ret = stun_message_init (msg, STUN_INDICATION, m, id);
429 if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
430 agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
431 uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
432 memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
440 bool stun_agent_init_response (StunAgent *agent, StunMessage *msg,
441 uint8_t *buffer, size_t buffer_len, const StunMessage *request)
444 StunTransactionId id;
446 if (stun_message_get_class (request) != STUN_REQUEST) {
450 msg->buffer = buffer;
451 msg->buffer_len = buffer_len;
453 msg->key = request->key;
454 msg->key_len = request->key_len;
455 memmove (msg->long_term_key, request->long_term_key,
456 sizeof(msg->long_term_key));
457 msg->long_term_valid = request->long_term_valid;
459 stun_message_id (request, id);
461 if (stun_message_init (msg, STUN_RESPONSE,
462 stun_message_get_method (request), id)) {
464 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
465 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
466 (agent->software_attribute != NULL ||
467 agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
468 stun_message_append_software (msg, agent->software_attribute);
476 bool stun_agent_init_error (StunAgent *agent, StunMessage *msg,
477 uint8_t *buffer, size_t buffer_len, const StunMessage *request,
480 StunTransactionId id;
482 if (stun_message_get_class (request) != STUN_REQUEST) {
486 msg->buffer = buffer;
487 msg->buffer_len = buffer_len;
489 msg->key = request->key;
490 msg->key_len = request->key_len;
491 memmove (msg->long_term_key, request->long_term_key,
492 sizeof(msg->long_term_key));
493 msg->long_term_valid = request->long_term_valid;
495 stun_message_id (request, id);
498 if (stun_message_init (msg, STUN_ERROR,
499 stun_message_get_method (request), id)) {
501 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
502 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
503 (agent->software_attribute != NULL ||
504 agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
505 stun_message_append_software (msg, agent->software_attribute);
507 if (stun_message_append_error (msg, err) == STUN_MESSAGE_RETURN_SUCCESS) {
515 size_t stun_agent_build_unknown_attributes_error (StunAgent *agent,
516 StunMessage *msg, uint8_t *buffer, size_t buffer_len,
517 const StunMessage *request)
521 uint16_t ids[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES];
523 counter = stun_agent_find_unknowns (agent, request,
524 ids, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES);
526 if (stun_agent_init_error (agent, msg, buffer, buffer_len,
527 request, STUN_ERROR_UNKNOWN_ATTRIBUTE) == FALSE) {
531 /* NOTE: Old RFC3489 compatibility:
532 * When counter is odd, duplicate one value for 32-bits padding. */
533 if (!stun_message_has_cookie (request) && (counter & 1))
534 ids[counter++] = ids[0];
536 if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
537 ids, counter * 2) == STUN_MESSAGE_RETURN_SUCCESS) {
538 return stun_agent_finish_message (agent, msg, request->key, request->key_len);
545 size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
546 const uint8_t *key, size_t key_len)
550 int saved_id_idx = 0;
552 bool remember_transaction;
554 remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST);
556 if (agent->compatibility == STUN_COMPATIBILITY_OC2007 &&
557 stun_message_get_method (msg) == STUN_SEND) {
558 /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to
559 * STUN_SEND requests, so don't bother waiting for them. More details at
560 * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx.
562 remember_transaction = FALSE;
565 if (remember_transaction) {
566 for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) {
567 if (agent->sent_ids[saved_id_idx].valid == FALSE) {
572 if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) {
573 stun_debug ("WARNING: Saved IDs full. STUN message dropped.");
577 if (msg->key != NULL) {
579 key_len = msg->key_len;
585 if (msg->long_term_valid) {
586 memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key));
587 } else if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
588 uint8_t *realm = NULL;
589 uint8_t *username = NULL;
591 uint16_t username_len;
593 realm = (uint8_t *) stun_message_find (msg,
594 STUN_ATTRIBUTE_REALM, &realm_len);
595 username = (uint8_t *) stun_message_find (msg,
596 STUN_ATTRIBUTE_USERNAME, &username_len);
597 if (username == NULL || realm == NULL) {
600 stun_hash_creds (realm, realm_len,
601 username, username_len,
603 memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key));
604 msg->long_term_valid = TRUE;
608 /* If no realm/username and long term credentials,
609 then don't send the message integrity */
611 ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20);
615 if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) {
616 if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
617 agent->compatibility == STUN_COMPATIBILITY_OC2007) {
618 stun_sha1 (msg->buffer, stun_message_length (msg),
619 stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE);
620 } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
622 if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
625 stun_sha1 (msg->buffer, stun_message_length (msg),
626 stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE);
628 stun_sha1 (msg->buffer, stun_message_length (msg),
629 stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE);
632 if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 ||
633 agent->compatibility == STUN_COMPATIBILITY_OC2007) {
634 stun_sha1 (msg->buffer, stun_message_length (msg),
635 stun_message_length (msg) - 20, ptr, key, key_len, TRUE);
636 } else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
638 if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT)
641 stun_sha1 (msg->buffer, stun_message_length (msg),
642 stun_message_length (msg) - minus, ptr, key, key_len, TRUE);
644 stun_sha1 (msg->buffer, stun_message_length (msg),
645 stun_message_length (msg) - 20, ptr, key, key_len, FALSE);
649 stun_debug (" Message HMAC-SHA1 message integrity:");
650 stun_debug_bytes (" key : ", key, key_len);
651 stun_debug_bytes (" sent : ", ptr, 20);
655 if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
656 agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
657 agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
658 ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4);
663 fpr = stun_fingerprint (msg->buffer, stun_message_length (msg), FALSE);
664 memcpy (ptr, &fpr, sizeof (fpr));
666 stun_debug_bytes (" Message HMAC-SHA1 fingerprint: ", ptr, 4);
670 if (remember_transaction) {
671 stun_message_id (msg, agent->sent_ids[saved_id_idx].id);
672 agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg);
673 agent->sent_ids[saved_id_idx].key = (uint8_t *) key;
674 agent->sent_ids[saved_id_idx].key_len = key_len;
675 memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key,
676 sizeof(msg->long_term_key));
677 agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid;
678 agent->sent_ids[saved_id_idx].valid = TRUE;
681 msg->key = (uint8_t *) key;
682 msg->key_len = key_len;
683 return stun_message_length (msg);
687 static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type)
690 uint16_t *known_attr = agent->known_attributes;
692 while(*known_attr != 0) {
693 if (*known_attr == type) {
705 stun_agent_find_unknowns (StunAgent *agent, const StunMessage * msg,
706 uint16_t *list, unsigned max)
709 uint16_t len = stun_message_length (msg);
712 offset = STUN_MESSAGE_ATTRIBUTES_POS;
714 while ((offset < len) && (count < max))
716 size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
717 uint16_t atype = stun_getw (msg->buffer + offset);
719 if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype))
721 stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)",
722 (unsigned)atype, (unsigned)alen);
723 list[count++] = htons (atype);
726 if (!(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES))
727 alen = stun_align (alen);
729 offset += STUN_ATTRIBUTE_VALUE_POS + alen;
732 stun_debug ("STUN unknown: %u mandatory attribute(s)!", count);
736 void stun_agent_set_software (StunAgent *agent, const char *software)
738 agent->software_attribute = software;