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"
47 #include <sys/types.h>
48 #include <sys/socket.h>
55 bool stun_message_init (StunMessage
*msg
, StunClass c
, StunMethod m
,
56 const StunTransactionId id
)
59 if (msg
->buffer_len
< STUN_MESSAGE_HEADER_LENGTH
)
62 memset (msg
->buffer
, 0, 4);
63 stun_set_type (msg
->buffer
, c
, m
);
65 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
,
66 id
, STUN_MESSAGE_TRANS_ID_LEN
);
71 uint16_t stun_message_length (const StunMessage
*msg
)
73 return stun_getw (msg
->buffer
+ STUN_MESSAGE_LENGTH_POS
) +
74 STUN_MESSAGE_HEADER_LENGTH
;
81 stun_message_find (const StunMessage
*msg
, StunAttribute type
,
84 size_t length
= stun_message_length (msg
);
88 offset
= STUN_MESSAGE_ATTRIBUTES_POS
;
90 while (offset
< length
)
92 uint16_t atype
= stun_getw (msg
->buffer
+ offset
);
93 size_t alen
= stun_getw (msg
->buffer
+ offset
+ STUN_ATTRIBUTE_TYPE_LEN
);
96 offset
+= STUN_ATTRIBUTE_VALUE_POS
;
101 return msg
->buffer
+ offset
;
104 /* Look for and ignore misordered attributes */
107 case STUN_ATTRIBUTE_MESSAGE_INTEGRITY
:
108 /* Only fingerprint may come after M-I */
109 if (type
== STUN_ATTRIBUTE_FINGERPRINT
)
112 case STUN_ATTRIBUTE_FINGERPRINT
:
113 /* Nothing may come after FPR */
117 alen
= stun_align (alen
);
126 stun_message_find_flag (const StunMessage
*msg
, StunAttribute type
)
131 ptr
= stun_message_find (msg
, type
, &len
);
133 return STUN_MESSAGE_RETURN_NOT_FOUND
;
134 return (len
== 0) ? STUN_MESSAGE_RETURN_SUCCESS
:
135 STUN_MESSAGE_RETURN_INVALID
;
140 stun_message_find32 (const StunMessage
*msg
, StunAttribute type
,
146 ptr
= stun_message_find (msg
, type
, &len
);
148 return STUN_MESSAGE_RETURN_NOT_FOUND
;
154 memcpy (&val
, ptr
, sizeof (val
));
156 return STUN_MESSAGE_RETURN_SUCCESS
;
158 return STUN_MESSAGE_RETURN_INVALID
;
163 stun_message_find64 (const StunMessage
*msg
, StunAttribute type
,
169 ptr
= stun_message_find (msg
, type
, &len
);
171 return STUN_MESSAGE_RETURN_NOT_FOUND
;
177 memcpy (tab
, ptr
, sizeof (tab
));
178 *pval
= ((uint64_t)ntohl (tab
[0]) << 32) | ntohl (tab
[1]);
179 return STUN_MESSAGE_RETURN_SUCCESS
;
181 return STUN_MESSAGE_RETURN_INVALID
;
186 stun_message_find_string (const StunMessage
*msg
, StunAttribute type
,
187 char *buf
, size_t buflen
)
189 const unsigned char *ptr
;
192 ptr
= stun_message_find (msg
, type
, &len
);
194 return STUN_MESSAGE_RETURN_NOT_FOUND
;
197 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
199 memcpy (buf
, ptr
, len
);
201 return STUN_MESSAGE_RETURN_SUCCESS
;
206 stun_message_find_addr (const StunMessage
*msg
, StunAttribute type
,
207 struct sockaddr
*addr
, socklen_t
*addrlen
)
212 ptr
= stun_message_find (msg
, type
, &len
);
214 return STUN_MESSAGE_RETURN_NOT_FOUND
;
217 return STUN_MESSAGE_RETURN_INVALID
;
223 struct sockaddr_in
*ip4
= (struct sockaddr_in
*)addr
;
224 if (((size_t) *addrlen
< sizeof (*ip4
)) || (len
!= 8))
226 *addrlen
= sizeof (*ip4
);
227 return STUN_MESSAGE_RETURN_INVALID
;
230 memset (ip4
, 0, *addrlen
);
231 ip4
->sin_family
= AF_INET
;
235 *addrlen
= sizeof (*ip4
);
236 memcpy (&ip4
->sin_port
, ptr
+ 2, 2);
237 memcpy (&ip4
->sin_addr
, ptr
+ 4, 4);
238 return STUN_MESSAGE_RETURN_SUCCESS
;
243 struct sockaddr_in6
*ip6
= (struct sockaddr_in6
*)addr
;
244 if (((size_t) *addrlen
< sizeof (*ip6
)) || (len
!= 20))
246 *addrlen
= sizeof (*ip6
);
247 return STUN_MESSAGE_RETURN_INVALID
;
250 memset (ip6
, 0, *addrlen
);
251 ip6
->sin6_family
= AF_INET6
;
255 *addrlen
= sizeof (*ip6
);
256 memcpy (&ip6
->sin6_port
, ptr
+ 2, 2);
257 memcpy (&ip6
->sin6_addr
, ptr
+ 4, 16);
258 return STUN_MESSAGE_RETURN_SUCCESS
;
262 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS
;
266 stun_message_find_xor_addr (const StunMessage
*msg
, StunAttribute type
,
267 struct sockaddr
*addr
,
270 StunMessageReturn val
= stun_message_find_addr (msg
, type
, addr
, addrlen
);
274 return stun_xor_address (msg
, addr
, *addrlen
, STUN_MAGIC_COOKIE
);
278 stun_message_find_xor_addr_full (const StunMessage
*msg
, StunAttribute type
,
279 struct sockaddr
*addr
, socklen_t
*addrlen
,
280 uint32_t magic_cookie
)
282 StunMessageReturn val
= stun_message_find_addr (msg
, type
, addr
, addrlen
);
286 return stun_xor_address (msg
, addr
, *addrlen
, magic_cookie
);
290 stun_message_find_error (const StunMessage
*msg
, int *code
)
293 const uint8_t *ptr
= stun_message_find (msg
, STUN_ATTRIBUTE_ERROR_CODE
, &alen
);
294 uint8_t class, number
;
297 return STUN_MESSAGE_RETURN_NOT_FOUND
;
299 return STUN_MESSAGE_RETURN_INVALID
;
301 class = ptr
[2] & 0x7;
303 if ((class < 3) || (class > 6) || (number
> 99))
304 return STUN_MESSAGE_RETURN_INVALID
;
306 *code
= (class * 100) + number
;
307 return STUN_MESSAGE_RETURN_SUCCESS
;
311 stun_message_append (StunMessage
*msg
, StunAttribute type
, size_t length
)
314 uint16_t mlen
= stun_message_length (msg
);
316 if ((size_t)mlen
+ STUN_ATTRIBUTE_HEADER_LENGTH
+ length
> msg
->buffer_len
)
320 a
= msg
->buffer
+ mlen
;
321 a
= stun_setw (a
, type
);
322 /* NOTE: If cookie is not present, we need to force the attribute length
323 * to a multiple of 4 for compatibility with old RFC3489 */
324 a
= stun_setw (a
, stun_message_has_cookie (msg
) ? length
: stun_align (length
));
327 /* Add padding if needed */
328 memset (a
+ length
, ' ', stun_padding (length
));
329 mlen
+= stun_padding (length
);
331 stun_setw (msg
->buffer
+ STUN_MESSAGE_LENGTH_POS
, mlen
- STUN_MESSAGE_HEADER_LENGTH
);
337 stun_message_append_bytes (StunMessage
*msg
, StunAttribute type
,
338 const void *data
, size_t len
)
340 void *ptr
= stun_message_append (msg
, type
, len
);
342 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
344 memcpy (ptr
, data
, len
);
345 return STUN_MESSAGE_RETURN_SUCCESS
;
350 stun_message_append_flag (StunMessage
*msg
, StunAttribute type
)
352 return stun_message_append_bytes (msg
, type
, NULL
, 0);
357 stun_message_append32 (StunMessage
*msg
, StunAttribute type
,
360 value
= htonl (value
);
361 return stun_message_append_bytes (msg
, type
, &value
, 4);
366 stun_message_append64 (StunMessage
*msg
, StunAttribute type
,
370 tab
[0] = htonl ((uint32_t)(value
>> 32));
371 tab
[1] = htonl ((uint32_t)value
);
372 return stun_message_append_bytes (msg
, type
, tab
, 8);
377 stun_message_append_string (StunMessage
* msg
, StunAttribute type
,
380 return stun_message_append_bytes (msg
, type
, str
, strlen (str
));
384 stun_message_append_addr (StunMessage
*msg
, StunAttribute type
,
385 const struct sockaddr
*addr
, socklen_t addrlen
)
392 if ((size_t) addrlen
< sizeof (struct sockaddr
))
393 return STUN_MESSAGE_RETURN_INVALID
;
395 switch (addr
->sa_family
)
399 const struct sockaddr_in
*ip4
= (const struct sockaddr_in
*)addr
;
401 port
= ip4
->sin_port
;
409 const struct sockaddr_in6
*ip6
= (const struct sockaddr_in6
*)addr
;
410 if ((size_t) addrlen
< sizeof (*ip6
))
411 return STUN_MESSAGE_RETURN_INVALID
;
414 port
= ip6
->sin6_port
;
416 pa
= &ip6
->sin6_addr
;
421 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS
;
424 ptr
= stun_message_append (msg
, type
, 4 + alen
);
426 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
430 memcpy (ptr
+ 2, &port
, 2);
431 memcpy (ptr
+ 4, pa
, alen
);
432 return STUN_MESSAGE_RETURN_SUCCESS
;
437 stun_message_append_xor_addr (StunMessage
*msg
, StunAttribute type
,
438 const struct sockaddr
*addr
, socklen_t addrlen
)
440 StunMessageReturn val
;
441 /* Must be big enough to hold any supported address: */
442 struct sockaddr_storage
xor;
444 if ((size_t) addrlen
> sizeof (xor))
445 addrlen
= sizeof (xor);
446 memcpy (&xor, addr
, addrlen
);
448 val
= stun_xor_address (msg
, (struct sockaddr
*)&xor, addrlen
,
453 return stun_message_append_addr (msg
, type
, (struct sockaddr
*)&xor,
458 stun_message_append_xor_addr_full (StunMessage
*msg
, StunAttribute type
,
459 const struct sockaddr
*addr
, socklen_t addrlen
,
460 uint32_t magic_cookie
)
462 StunMessageReturn val
;
463 /* Must be big enough to hold any supported address: */
464 struct sockaddr_storage
xor;
466 if ((size_t) addrlen
> sizeof (xor))
467 addrlen
= sizeof (xor);
468 memcpy (&xor, addr
, addrlen
);
470 val
= stun_xor_address (msg
, (struct sockaddr
*)&xor, addrlen
, magic_cookie
);
474 return stun_message_append_addr (msg
, type
, (struct sockaddr
*)&xor,
481 stun_message_append_error (StunMessage
*msg
, StunError code
)
483 const char *str
= stun_strerror (code
);
484 size_t len
= strlen (str
);
485 div_t d
= div (code
, 100);
487 uint8_t *ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_ERROR_CODE
, 4 + len
);
489 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
494 memcpy (ptr
+ 4, str
, len
);
495 return STUN_MESSAGE_RETURN_SUCCESS
;
498 int stun_message_validate_buffer_length (const uint8_t *msg
, size_t length
)
505 stun_debug ("STUN error: No data!\n");
506 return STUN_MESSAGE_BUFFER_INVALID
;
511 stun_debug ("STUN error: RTP or other non-protocol packet!\n");
512 return STUN_MESSAGE_BUFFER_INVALID
; // RTP or other non-STUN packet
517 stun_debug ("STUN error: Incomplete STUN message header!\n");
518 return STUN_MESSAGE_BUFFER_INCOMPLETE
;
521 mlen
= stun_getw (msg
+ STUN_MESSAGE_LENGTH_POS
) +
522 STUN_MESSAGE_HEADER_LENGTH
;
524 if (stun_padding (mlen
))
526 stun_debug ("STUN error: Invalid message length: %u!\n", (unsigned)mlen
);
527 return STUN_MESSAGE_BUFFER_INVALID
; // wrong padding
532 stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n",
533 (unsigned)length
, (unsigned)mlen
);
534 return STUN_MESSAGE_BUFFER_INCOMPLETE
; // partial message
540 /* from then on, we know we have the entire packet in buffer */
543 size_t alen
= stun_align (stun_getw (msg
+ STUN_ATTRIBUTE_TYPE_LEN
));
545 /* thanks to padding check, if (end > msg) then there is not only one
546 * but at least 4 bytes left */
551 stun_debug ("STUN error: %u instead of %u bytes for attribute!\n",
552 (unsigned)len
, (unsigned)alen
);
553 return STUN_MESSAGE_BUFFER_INVALID
; // no room for attribute value + padding
563 void stun_message_id (const StunMessage
*msg
, StunTransactionId id
)
565 memcpy (id
, msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, STUN_MESSAGE_TRANS_ID_LEN
);
568 StunMethod
stun_message_get_method (const StunMessage
*msg
)
570 uint16_t t
= stun_getw (msg
->buffer
);
572 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
573 which states that 8th and 12th bits are for the class and that 0x01 is
575 So 0x0115 is reported as a "connect error response", while it should be
576 a data indication, which message type should actually be 0x0017
577 This should fix the issue, and it's considered safe since the "connect"
578 method doesn't exist anymore */
581 return (StunMethod
)(((t
& 0x3e00) >> 2) | ((t
& 0x00e0) >> 1) |
586 StunClass
stun_message_get_class (const StunMessage
*msg
)
588 uint16_t t
= stun_getw (msg
->buffer
);
590 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
591 which states that 8th and 12th bits are for the class and that 0x01 is
593 So 0x0115 is reported as a "connect error response", while it should be
594 a data indication, which message type should actually be 0x0017
595 This should fix the issue, and it's considered safe since the "connect"
596 method doesn't exist anymore */
599 return (StunClass
)(((t
& 0x0100) >> 7) | ((t
& 0x0010) >> 4));
602 bool stun_message_has_attribute (const StunMessage
*msg
, StunAttribute type
)
605 return stun_message_find (msg
, type
, &dummy
) != NULL
;
609 bool stun_optional (uint16_t t
)
611 return (t
>> 15) == 1;
614 const char *stun_strerror (StunError code
)
622 { STUN_ERROR_TRY_ALTERNATE
, "Try alternate server" },
623 { STUN_ERROR_BAD_REQUEST
, "Bad request" },
624 { STUN_ERROR_UNAUTHORIZED
, "Unauthorized" },
625 { STUN_ERROR_UNKNOWN_ATTRIBUTE
, "Unknown Attribute" },
626 { STUN_ERROR_ALLOCATION_MISMATCH
, "Allocation Mismatch" },
627 { STUN_ERROR_STALE_NONCE
, "Stale Nonce" },
628 { STUN_ERROR_ACT_DST_ALREADY
, "Active Destination Already Set" },
629 { STUN_ERROR_UNSUPPORTED_FAMILY
, "Address Family not Supported" },
630 { STUN_ERROR_UNSUPPORTED_TRANSPORT
, "Unsupported Transport Protocol" },
631 { STUN_ERROR_INVALID_IP
, "Invalid IP Address" },
632 { STUN_ERROR_INVALID_PORT
, "Invalid Port" },
633 { STUN_ERROR_OP_TCP_ONLY
, "Operation for TCP Only" },
634 { STUN_ERROR_CONN_ALREADY
, "Connection Already Exists" },
635 { STUN_ERROR_ALLOCATION_QUOTA_REACHED
, "Allocation Quota Reached" },
636 { STUN_ERROR_ROLE_CONFLICT
, "Role conflict" },
637 { STUN_ERROR_SERVER_ERROR
, "Server Error" },
638 { STUN_ERROR_SERVER_CAPACITY
, "Insufficient Capacity" },
639 { STUN_ERROR_INSUFFICIENT_CAPACITY
, "Insufficient Capacity" },
641 const char *str
= "Unknown error";
644 for (i
= 0; i
< (sizeof (tab
) / sizeof (tab
[0])); i
++)
646 if (tab
[i
].code
== code
)
653 /* Maximum allowed error message length */
654 // assert (strlen (str) < 128);