2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008-2009 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2007-2009 Nokia Corporation. All rights reserved.
7 * Contact: Rémi Denis-Courmont
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
25 * Youness Alaoui, Collabora Ltd.
26 * Rémi Denis-Courmont, Nokia
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
43 #include "stunmessage.h"
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
59 bool stun_message_init (StunMessage *msg, StunClass c, StunMethod m,
60 const StunTransactionId id)
63 if (msg->buffer_len < STUN_MESSAGE_HEADER_LENGTH)
66 memset (msg->buffer, 0, 4);
67 stun_set_type (msg->buffer, c, m);
69 memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS,
70 id, STUN_MESSAGE_TRANS_ID_LEN);
75 uint16_t stun_message_length (const StunMessage *msg)
77 return stun_getw (msg->buffer + STUN_MESSAGE_LENGTH_POS) +
78 STUN_MESSAGE_HEADER_LENGTH;
85 stun_message_find (const StunMessage *msg, StunAttribute type,
88 size_t length = stun_message_length (msg);
91 /* In MS-TURN, IDs of REALM and NONCE STUN attributes are swapped. */
92 if (msg->agent && msg->agent->compatibility == STUN_COMPATIBILITY_OC2007)
94 if (type == STUN_ATTRIBUTE_REALM)
95 type = STUN_ATTRIBUTE_NONCE;
96 else if (type == STUN_ATTRIBUTE_NONCE)
97 type = STUN_ATTRIBUTE_REALM;
100 offset = STUN_MESSAGE_ATTRIBUTES_POS;
102 while (offset < length)
104 uint16_t atype = stun_getw (msg->buffer + offset);
105 size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
108 offset += STUN_ATTRIBUTE_VALUE_POS;
113 return msg->buffer + offset;
116 /* Look for and ignore misordered attributes */
119 case STUN_ATTRIBUTE_MESSAGE_INTEGRITY:
120 /* Only fingerprint may come after M-I */
121 if (type == STUN_ATTRIBUTE_FINGERPRINT)
125 case STUN_ATTRIBUTE_FINGERPRINT:
126 /* Nothing may come after FPR */
130 /* Nothing misordered. */
135 (msg->agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES)))
136 alen = stun_align (alen);
146 stun_message_find_flag (const StunMessage *msg, StunAttribute type)
151 ptr = stun_message_find (msg, type, &len);
153 return STUN_MESSAGE_RETURN_NOT_FOUND;
154 return (len == 0) ? STUN_MESSAGE_RETURN_SUCCESS :
155 STUN_MESSAGE_RETURN_INVALID;
160 stun_message_find32 (const StunMessage *msg, StunAttribute type,
166 ptr = stun_message_find (msg, type, &len);
168 return STUN_MESSAGE_RETURN_NOT_FOUND;
174 memcpy (&val, ptr, sizeof (val));
176 return STUN_MESSAGE_RETURN_SUCCESS;
178 return STUN_MESSAGE_RETURN_INVALID;
183 stun_message_find64 (const StunMessage *msg, StunAttribute type,
189 ptr = stun_message_find (msg, type, &len);
191 return STUN_MESSAGE_RETURN_NOT_FOUND;
197 memcpy (tab, ptr, sizeof (tab));
198 *pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]);
199 return STUN_MESSAGE_RETURN_SUCCESS;
201 return STUN_MESSAGE_RETURN_INVALID;
206 stun_message_find_string (const StunMessage *msg, StunAttribute type,
207 char *buf, size_t buflen)
209 const unsigned char *ptr;
212 ptr = stun_message_find (msg, type, &len);
214 return STUN_MESSAGE_RETURN_NOT_FOUND;
217 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
219 memcpy (buf, ptr, len);
221 return STUN_MESSAGE_RETURN_SUCCESS;
226 stun_message_find_addr (const StunMessage *msg, StunAttribute type,
227 struct sockaddr_storage *addr, socklen_t *addrlen)
232 ptr = stun_message_find (msg, type, &len);
234 return STUN_MESSAGE_RETURN_NOT_FOUND;
237 return STUN_MESSAGE_RETURN_INVALID;
243 struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
244 if (((size_t) *addrlen < sizeof (*ip4)) || (len != 8))
246 *addrlen = sizeof (*ip4);
247 return STUN_MESSAGE_RETURN_INVALID;
250 memset (ip4, 0, *addrlen);
251 ip4->sin_family = AF_INET;
255 *addrlen = sizeof (*ip4);
256 memcpy (&ip4->sin_port, ptr + 2, 2);
257 memcpy (&ip4->sin_addr, ptr + 4, 4);
258 return STUN_MESSAGE_RETURN_SUCCESS;
263 struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
264 if (((size_t) *addrlen < sizeof (*ip6)) || (len != 20))
266 *addrlen = sizeof (*ip6);
267 return STUN_MESSAGE_RETURN_INVALID;
270 memset (ip6, 0, *addrlen);
271 ip6->sin6_family = AF_INET6;
275 *addrlen = sizeof (*ip6);
276 memcpy (&ip6->sin6_port, ptr + 2, 2);
277 memcpy (&ip6->sin6_addr, ptr + 4, 16);
278 return STUN_MESSAGE_RETURN_SUCCESS;
282 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
287 stun_message_find_xor_addr (const StunMessage *msg, StunAttribute type,
288 struct sockaddr_storage *addr, socklen_t *addrlen)
290 StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
294 return stun_xor_address (msg, addr, *addrlen, STUN_MAGIC_COOKIE);
298 stun_message_find_xor_addr_full (const StunMessage *msg, StunAttribute type,
299 struct sockaddr_storage *addr, socklen_t *addrlen, uint32_t magic_cookie)
301 StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
305 return stun_xor_address (msg, addr, *addrlen, magic_cookie);
309 stun_message_find_error (const StunMessage *msg, int *code)
312 const uint8_t *ptr = stun_message_find (msg, STUN_ATTRIBUTE_ERROR_CODE, &alen);
313 uint8_t class, number;
316 return STUN_MESSAGE_RETURN_NOT_FOUND;
318 return STUN_MESSAGE_RETURN_INVALID;
320 class = ptr[2] & 0x7;
322 if ((class < 3) || (class > 6) || (number > 99))
323 return STUN_MESSAGE_RETURN_INVALID;
325 *code = (class * 100) + number;
326 return STUN_MESSAGE_RETURN_SUCCESS;
330 stun_message_append (StunMessage *msg, StunAttribute type, size_t length)
333 uint16_t mlen = stun_message_length (msg);
335 /* In MS-TURN, IDs of REALM and NONCE STUN attributes are swapped. */
336 if (msg->agent && msg->agent->compatibility == STUN_COMPATIBILITY_OC2007)
338 if (type == STUN_ATTRIBUTE_NONCE)
339 type = STUN_ATTRIBUTE_REALM;
340 else if (type == STUN_ATTRIBUTE_REALM)
341 type = STUN_ATTRIBUTE_NONCE;
344 if ((size_t)mlen + STUN_ATTRIBUTE_HEADER_LENGTH + length > msg->buffer_len)
348 a = msg->buffer + mlen;
349 a = stun_setw (a, type);
351 (msg->agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES))
353 a = stun_setw (a, length);
355 /* NOTE: If cookie is not present, we need to force the attribute length
356 * to a multiple of 4 for compatibility with old RFC3489 */
357 a = stun_setw (a, stun_message_has_cookie (msg) ? length : stun_align (length));
359 /* Add padding if needed. Avoid a zero-length memset() call. */
360 if (stun_padding (length) > 0) {
361 memset (a + length, ' ', stun_padding (length));
362 mlen += stun_padding (length);
368 stun_setw (msg->buffer + STUN_MESSAGE_LENGTH_POS, mlen - STUN_MESSAGE_HEADER_LENGTH);
374 stun_message_append_bytes (StunMessage *msg, StunAttribute type,
375 const void *data, size_t len)
377 void *ptr = stun_message_append (msg, type, len);
379 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
382 memcpy (ptr, data, len);
384 return STUN_MESSAGE_RETURN_SUCCESS;
389 stun_message_append_flag (StunMessage *msg, StunAttribute type)
391 return stun_message_append_bytes (msg, type, NULL, 0);
396 stun_message_append32 (StunMessage *msg, StunAttribute type,
399 value = htonl (value);
400 return stun_message_append_bytes (msg, type, &value, 4);
405 stun_message_append64 (StunMessage *msg, StunAttribute type,
409 tab[0] = htonl ((uint32_t)(value >> 32));
410 tab[1] = htonl ((uint32_t)value);
411 return stun_message_append_bytes (msg, type, tab, 8);
416 stun_message_append_string (StunMessage * msg, StunAttribute type,
419 return stun_message_append_bytes (msg, type, str, strlen (str));
423 stun_message_append_addr (StunMessage *msg, StunAttribute type,
424 const struct sockaddr *addr, socklen_t addrlen)
432 const struct sockaddr *addr;
433 const struct sockaddr_in *in;
434 const struct sockaddr_in6 *in6;
437 if ((size_t) addrlen < sizeof (struct sockaddr))
438 return STUN_MESSAGE_RETURN_INVALID;
442 switch (addr->sa_family)
446 const struct sockaddr_in *ip4 = sa.in;
448 port = ip4->sin_port;
456 const struct sockaddr_in6 *ip6 = sa.in6;
457 if ((size_t) addrlen < sizeof (*ip6))
458 return STUN_MESSAGE_RETURN_INVALID;
461 port = ip6->sin6_port;
463 pa = &ip6->sin6_addr;
468 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
471 ptr = stun_message_append (msg, type, 4 + alen);
473 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
477 memcpy (ptr + 2, &port, 2);
478 memcpy (ptr + 4, pa, alen);
479 return STUN_MESSAGE_RETURN_SUCCESS;
484 stun_message_append_xor_addr (StunMessage *msg, StunAttribute type,
485 const struct sockaddr_storage *addr, socklen_t addrlen)
487 StunMessageReturn val;
488 /* Must be big enough to hold any supported address: */
489 struct sockaddr_storage tmpaddr;
491 if ((size_t) addrlen > sizeof (tmpaddr))
492 addrlen = sizeof (tmpaddr);
493 memcpy (&tmpaddr, addr, addrlen);
495 val = stun_xor_address (msg, &tmpaddr, addrlen,
500 return stun_message_append_addr (msg, type, (struct sockaddr *) &tmpaddr,
505 stun_message_append_xor_addr_full (StunMessage *msg, StunAttribute type,
506 const struct sockaddr_storage *addr, socklen_t addrlen,
507 uint32_t magic_cookie)
509 StunMessageReturn val;
510 /* Must be big enough to hold any supported address: */
511 struct sockaddr_storage tmpaddr;
513 if ((size_t) addrlen > sizeof (tmpaddr))
514 addrlen = sizeof (tmpaddr);
515 memcpy (&tmpaddr, addr, addrlen);
517 val = stun_xor_address (msg, &tmpaddr, addrlen, magic_cookie);
521 return stun_message_append_addr (msg, type, (struct sockaddr *) &tmpaddr,
528 stun_message_append_error (StunMessage *msg, StunError code)
530 const char *str = stun_strerror (code);
531 size_t len = strlen (str);
533 uint8_t *ptr = stun_message_append (msg, STUN_ATTRIBUTE_ERROR_CODE, 4 + len);
535 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
540 memcpy (ptr + 4, str, len);
541 return STUN_MESSAGE_RETURN_SUCCESS;
544 /* Fast validity check for a potential STUN packet. Examines the type and
545 * length, but none of the attributes. Designed to allow vectored I/O on all
546 * incoming packets, filtering packets for closer inspection as to whether
547 * they’re STUN packets. If they look like they might be, their buffers are
548 * compacted to allow a more thorough check. */
549 ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers,
550 int n_buffers, size_t total_length, bool has_padding)
554 if (total_length < 1 || n_buffers == 0 || buffers[0].buffer == NULL)
556 stun_debug ("STUN error: No data!");
557 return STUN_MESSAGE_BUFFER_INVALID;
560 if (buffers[0].buffer[0] >> 6)
562 return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
565 if (total_length < STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN)
567 stun_debug ("STUN error: Incomplete STUN message header!");
568 return STUN_MESSAGE_BUFFER_INCOMPLETE;
571 if (buffers[0].size >= STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN) {
573 mlen = stun_getw (buffers[0].buffer + STUN_MESSAGE_LENGTH_POS);
575 /* Slow path. Tiny buffers abound. */
576 size_t skip_remaining = STUN_MESSAGE_LENGTH_POS;
580 for (i = 0; (n_buffers >= 0 && i < (unsigned int) n_buffers) ||
581 (n_buffers < 0 && buffers[i].buffer != NULL); i++) {
582 if (buffers[i].size <= skip_remaining)
583 skip_remaining -= buffers[i].size;
588 /* Read bytes. May be split over two buffers. We’ve already checked that
589 * @total_length is long enough, so @n_buffers should be too. */
590 if (buffers[i].size - skip_remaining > 1) {
591 mlen = stun_getw (buffers[i].buffer + skip_remaining);
593 mlen = (*(buffers[i].buffer + skip_remaining) << 8) |
594 (*(buffers[i + 1].buffer));
598 mlen += STUN_MESSAGE_HEADER_LENGTH;
600 if (has_padding && stun_padding (mlen)) {
601 stun_debug ("STUN error: Invalid message length: %u!", (unsigned)mlen);
602 return STUN_MESSAGE_BUFFER_INVALID; // wrong padding
605 if (total_length < mlen) {
606 stun_debug ("STUN error: Incomplete message: %u of %u bytes!",
607 (unsigned) total_length, (unsigned) mlen);
608 return STUN_MESSAGE_BUFFER_INCOMPLETE; // partial message
614 int stun_message_validate_buffer_length (const uint8_t *msg, size_t length,
620 StunInputVector input_buffer = { msg, length };
622 /* Fast pre-check first. */
623 fast_retval = stun_message_validate_buffer_length_fast (&input_buffer, 1,
624 length, has_padding);
625 if (fast_retval <= 0)
630 /* Skip past the header (validated above). */
634 /* from then on, we know we have the entire packet in buffer */
641 stun_debug ("STUN error: Incomplete STUN attribute header of length "
642 "%u bytes!", (unsigned)len);
643 return STUN_MESSAGE_BUFFER_INVALID;
646 alen = stun_getw (msg + STUN_ATTRIBUTE_TYPE_LEN);
648 alen = stun_align (alen);
650 /* thanks to padding check, if (end > msg) then there is not only one
651 * but at least 4 bytes left */
656 stun_debug ("STUN error: %u instead of %u bytes for attribute!",
657 (unsigned)len, (unsigned)alen);
658 return STUN_MESSAGE_BUFFER_INVALID; // no room for attribute value + padding
668 void stun_message_id (const StunMessage *msg, StunTransactionId id)
670 memcpy (id, msg->buffer + STUN_MESSAGE_TRANS_ID_POS, STUN_MESSAGE_TRANS_ID_LEN);
673 StunMethod stun_message_get_method (const StunMessage *msg)
675 uint16_t t = stun_getw (msg->buffer);
677 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
678 which states that 8th and 12th bits are for the class and that 0x01 is
680 So 0x0115 is reported as a "connect error response", while it should be
681 a data indication, which message type should actually be 0x0017
682 This should fix the issue, and it's considered safe since the "connect"
683 method doesn't exist anymore */
686 return (StunMethod)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
691 StunClass stun_message_get_class (const StunMessage *msg)
693 uint16_t t = stun_getw (msg->buffer);
695 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
696 which states that 8th and 12th bits are for the class and that 0x01 is
698 So 0x0115 is reported as a "connect error response", while it should be
699 a data indication, which message type should actually be 0x0017
700 This should fix the issue, and it's considered safe since the "connect"
701 method doesn't exist anymore */
704 return (StunClass)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
707 bool stun_message_has_attribute (const StunMessage *msg, StunAttribute type)
710 return stun_message_find (msg, type, &dummy) != NULL;
714 bool stun_optional (uint16_t t)
716 return (t >> 15) == 1;
719 const char *stun_strerror (StunError code)
727 { STUN_ERROR_TRY_ALTERNATE, "Try alternate server" },
728 { STUN_ERROR_BAD_REQUEST, "Bad request" },
729 { STUN_ERROR_UNAUTHORIZED, "Unauthorized" },
730 { STUN_ERROR_UNKNOWN_ATTRIBUTE, "Unknown Attribute" },
731 { STUN_ERROR_ALLOCATION_MISMATCH, "Allocation Mismatch" },
732 { STUN_ERROR_STALE_NONCE, "Stale Nonce" },
733 { STUN_ERROR_ACT_DST_ALREADY, "Active Destination Already Set" },
734 { STUN_ERROR_UNSUPPORTED_FAMILY, "Address Family not Supported" },
735 { STUN_ERROR_UNSUPPORTED_TRANSPORT, "Unsupported Transport Protocol" },
736 { STUN_ERROR_INVALID_IP, "Invalid IP Address" },
737 { STUN_ERROR_INVALID_PORT, "Invalid Port" },
738 { STUN_ERROR_OP_TCP_ONLY, "Operation for TCP Only" },
739 { STUN_ERROR_CONN_ALREADY, "Connection Already Exists" },
740 { STUN_ERROR_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached" },
741 { STUN_ERROR_ROLE_CONFLICT, "Role conflict" },
742 { STUN_ERROR_SERVER_ERROR, "Server Error" },
743 { STUN_ERROR_SERVER_CAPACITY, "Insufficient Capacity" },
744 { STUN_ERROR_INSUFFICIENT_CAPACITY, "Insufficient Capacity" },
746 const char *str = "Unknown error";
749 for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
751 if (tab[i].code == code)
758 /* Maximum allowed error message length */
759 // assert (strlen (str) < 128);