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"
46 #include <sys/types.h>
47 #include <sys/socket.h>
54 bool stun_message_init (StunMessage
*msg
, stun_class_t c
, stun_method_t m
,
55 const stun_transid_t id
)
58 if (msg
->buffer_len
< STUN_MESSAGE_HEADER_LENGTH
)
61 memset (msg
->buffer
, 0, 4);
62 stun_set_type (msg
->buffer
, c
, m
);
64 memcpy (msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
,
65 id
, STUN_MESSAGE_TRANS_ID_LEN
);
70 uint16_t stun_message_length (const StunMessage
*msg
)
72 return stun_getw (msg
->buffer
+ STUN_MESSAGE_LENGTH_POS
) +
73 STUN_MESSAGE_HEADER_LENGTH
;
80 stun_message_find (const StunMessage
*msg
, stun_attr_type_t type
,
83 size_t length
= stun_message_length (msg
);
87 offset
= STUN_MESSAGE_ATTRIBUTES_POS
;
89 while (offset
< length
)
91 uint16_t atype
= stun_getw (msg
->buffer
+ offset
);
92 size_t alen
= stun_getw (msg
->buffer
+ offset
+ STUN_ATTRIBUTE_TYPE_LEN
);
95 offset
+= STUN_ATTRIBUTE_VALUE_POS
;
100 return msg
->buffer
+ offset
;
103 /* Look for and ignore misordered attributes */
106 case STUN_ATTRIBUTE_MESSAGE_INTEGRITY
:
107 /* Only fingerprint may come after M-I */
108 if (type
== STUN_ATTRIBUTE_FINGERPRINT
)
111 case STUN_ATTRIBUTE_FINGERPRINT
:
112 /* Nothing may come after FPR */
116 alen
= stun_align (alen
);
125 stun_message_find_flag (const StunMessage
*msg
, stun_attr_type_t type
)
130 ptr
= stun_message_find (msg
, type
, &len
);
132 return STUN_MESSAGE_RETURN_NOT_FOUND
;
133 return (len
== 0) ? STUN_MESSAGE_RETURN_SUCCESS
:
134 STUN_MESSAGE_RETURN_INVALID
;
139 stun_message_find32 (const StunMessage
*msg
, stun_attr_type_t type
,
145 ptr
= stun_message_find (msg
, type
, &len
);
147 return STUN_MESSAGE_RETURN_NOT_FOUND
;
153 memcpy (&val
, ptr
, sizeof (val
));
155 return STUN_MESSAGE_RETURN_SUCCESS
;
157 return STUN_MESSAGE_RETURN_INVALID
;
162 stun_message_find64 (const StunMessage
*msg
, stun_attr_type_t type
,
168 ptr
= stun_message_find (msg
, type
, &len
);
170 return STUN_MESSAGE_RETURN_NOT_FOUND
;
176 memcpy (tab
, ptr
, sizeof (tab
));
177 *pval
= ((uint64_t)ntohl (tab
[0]) << 32) | ntohl (tab
[1]);
178 return STUN_MESSAGE_RETURN_SUCCESS
;
180 return STUN_MESSAGE_RETURN_INVALID
;
185 stun_message_find_string (const StunMessage
*msg
, stun_attr_type_t type
,
186 char *buf
, size_t buflen
)
188 const unsigned char *ptr
;
191 ptr
= stun_message_find (msg
, type
, &len
);
193 return STUN_MESSAGE_RETURN_NOT_FOUND
;
196 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
198 memcpy (buf
, ptr
, len
);
200 return STUN_MESSAGE_RETURN_SUCCESS
;
205 stun_message_find_addr (const StunMessage
*msg
, stun_attr_type_t type
,
206 struct sockaddr
*addr
, socklen_t
*addrlen
)
211 ptr
= stun_message_find (msg
, type
, &len
);
213 return STUN_MESSAGE_RETURN_NOT_FOUND
;
216 return STUN_MESSAGE_RETURN_INVALID
;
222 struct sockaddr_in
*ip4
= (struct sockaddr_in
*)addr
;
223 if (((size_t) *addrlen
< sizeof (*ip4
)) || (len
!= 8))
225 *addrlen
= sizeof (*ip4
);
226 return STUN_MESSAGE_RETURN_INVALID
;
229 memset (ip4
, 0, *addrlen
);
230 ip4
->sin_family
= AF_INET
;
234 *addrlen
= sizeof (*ip4
);
235 memcpy (&ip4
->sin_port
, ptr
+ 2, 2);
236 memcpy (&ip4
->sin_addr
, ptr
+ 4, 4);
237 return STUN_MESSAGE_RETURN_SUCCESS
;
242 struct sockaddr_in6
*ip6
= (struct sockaddr_in6
*)addr
;
243 if (((size_t) *addrlen
< sizeof (*ip6
)) || (len
!= 20))
245 *addrlen
= sizeof (*ip6
);
246 return STUN_MESSAGE_RETURN_INVALID
;
249 memset (ip6
, 0, *addrlen
);
250 ip6
->sin6_family
= AF_INET6
;
254 *addrlen
= sizeof (*ip6
);
255 memcpy (&ip6
->sin6_port
, ptr
+ 2, 2);
256 memcpy (&ip6
->sin6_addr
, ptr
+ 4, 16);
257 return STUN_MESSAGE_RETURN_SUCCESS
;
261 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS
;
265 stun_message_find_xor_addr (const StunMessage
*msg
, stun_attr_type_t type
,
266 struct sockaddr
*addr
,
269 StunMessageReturn val
= stun_message_find_addr (msg
, type
, addr
, addrlen
);
273 return stun_xor_address (msg
, addr
, *addrlen
, STUN_MAGIC_COOKIE
);
277 stun_message_find_xor_addr_full (const StunMessage
*msg
, stun_attr_type_t type
,
278 struct sockaddr
*addr
, socklen_t
*addrlen
,
279 uint32_t magic_cookie
)
281 StunMessageReturn val
= stun_message_find_addr (msg
, type
, addr
, addrlen
);
285 return stun_xor_address (msg
, addr
, *addrlen
, magic_cookie
);
289 stun_message_find_error (const StunMessage
*msg
, int *code
)
292 const uint8_t *ptr
= stun_message_find (msg
, STUN_ATTRIBUTE_ERROR_CODE
, &alen
);
293 uint8_t class, number
;
296 return STUN_MESSAGE_RETURN_NOT_FOUND
;
298 return STUN_MESSAGE_RETURN_INVALID
;
300 class = ptr
[2] & 0x7;
302 if ((class < 3) || (class > 6) || (number
> 99))
303 return STUN_MESSAGE_RETURN_INVALID
;
305 *code
= (class * 100) + number
;
306 return STUN_MESSAGE_RETURN_SUCCESS
;
310 * Reserves room for appending an attribute to an unfinished STUN message.
311 * @param msg STUN message buffer
312 * @param msize STUN message buffer size
313 * @param type message type (host byte order)
314 * @param length attribute payload byte length
315 * @return a pointer to an unitialized buffer of <length> bytes to
316 * where the attribute payload must be written, or NULL if there is not
317 * enough room in the STUN message buffer. Return value is always on a
321 stun_message_append (StunMessage
*msg
, stun_attr_type_t type
, size_t length
)
324 uint16_t mlen
= stun_message_length (msg
);
326 if ((size_t)mlen
+ STUN_ATTRIBUTE_HEADER_LENGTH
+ length
> msg
->buffer_len
)
330 a
= msg
->buffer
+ mlen
;
331 a
= stun_setw (a
, type
);
332 /* NOTE: If cookie is not present, we need to force the attribute length
333 * to a multiple of 4 for compatibility with old RFC3489 */
334 a
= stun_setw (a
, stun_has_cookie (msg
) ? length
: stun_align (length
));
337 /* Add padding if needed */
338 memset (a
+ length
, ' ', stun_padding (length
));
339 mlen
+= stun_padding (length
);
341 stun_setw (msg
->buffer
+ STUN_MESSAGE_LENGTH_POS
, mlen
- STUN_MESSAGE_HEADER_LENGTH
);
347 * Appends an attribute from memory.
348 * @param msg STUN message buffer
349 * @param msize STUN message buffer size
350 * @param type attribute type (host byte order)
351 * @param data memory address to copy payload from
352 * @param len attribute payload length
353 * @return 0 on success, ENOBUFS on error.
356 stun_message_append_bytes (StunMessage
*msg
, stun_attr_type_t type
,
357 const void *data
, size_t len
)
359 void *ptr
= stun_message_append (msg
, type
, len
);
361 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
363 memcpy (ptr
, data
, len
);
364 return STUN_MESSAGE_RETURN_SUCCESS
;
369 stun_message_append_flag (StunMessage
*msg
, stun_attr_type_t type
)
371 return stun_message_append_bytes (msg
, type
, NULL
, 0);
376 stun_message_append32 (StunMessage
*msg
, stun_attr_type_t type
,
379 value
= htonl (value
);
380 return stun_message_append_bytes (msg
, type
, &value
, 4);
385 stun_message_append64 (StunMessage
*msg
, stun_attr_type_t type
,
389 tab
[0] = htonl ((uint32_t)(value
>> 32));
390 tab
[1] = htonl ((uint32_t)value
);
391 return stun_message_append_bytes (msg
, type
, tab
, 8);
396 stun_message_append_string (StunMessage
* msg
, stun_attr_type_t type
,
399 return stun_message_append_bytes (msg
, type
, str
, strlen (str
));
403 stun_message_append_addr (StunMessage
*msg
, stun_attr_type_t type
,
404 const struct sockaddr
*addr
, socklen_t addrlen
)
411 if ((size_t) addrlen
< sizeof (struct sockaddr
))
412 return STUN_MESSAGE_RETURN_INVALID
;
414 switch (addr
->sa_family
)
418 const struct sockaddr_in
*ip4
= (const struct sockaddr_in
*)addr
;
420 port
= ip4
->sin_port
;
428 const struct sockaddr_in6
*ip6
= (const struct sockaddr_in6
*)addr
;
429 if ((size_t) addrlen
< sizeof (*ip6
))
430 return STUN_MESSAGE_RETURN_INVALID
;
433 port
= ip6
->sin6_port
;
435 pa
= &ip6
->sin6_addr
;
440 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS
;
443 ptr
= stun_message_append (msg
, type
, 4 + alen
);
445 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
449 memcpy (ptr
+ 2, &port
, 2);
450 memcpy (ptr
+ 4, pa
, alen
);
451 return STUN_MESSAGE_RETURN_SUCCESS
;
456 stun_message_append_xor_addr (StunMessage
*msg
, stun_attr_type_t type
,
457 const struct sockaddr
*addr
, socklen_t addrlen
)
459 StunMessageReturn val
;
460 /* Must be big enough to hold any supported address: */
461 struct sockaddr_storage
xor;
463 if ((size_t) addrlen
> sizeof (xor))
464 addrlen
= sizeof (xor);
465 memcpy (&xor, addr
, addrlen
);
467 val
= stun_xor_address (msg
, (struct sockaddr
*)&xor, addrlen
,
472 return stun_message_append_addr (msg
, type
, (struct sockaddr
*)&xor,
477 stun_message_append_xor_addr_full (StunMessage
*msg
, stun_attr_type_t type
,
478 const struct sockaddr
*addr
, socklen_t addrlen
,
479 uint32_t magic_cookie
)
481 StunMessageReturn val
;
482 /* Must be big enough to hold any supported address: */
483 struct sockaddr_storage
xor;
485 if ((size_t) addrlen
> sizeof (xor))
486 addrlen
= sizeof (xor);
487 memcpy (&xor, addr
, addrlen
);
489 val
= stun_xor_address (msg
, (struct sockaddr
*)&xor, addrlen
, magic_cookie
);
493 return stun_message_append_addr (msg
, type
, (struct sockaddr
*)&xor,
500 * Appends an ERROR-CODE attribute.
501 * @param msg STUN message buffer
502 * @param msize STUN message buffer size
503 * @param code STUN host-byte order integer error code
504 * @return 0 on success, or ENOBUFS otherwise
507 stun_message_append_error (StunMessage
*msg
, stun_error_t code
)
509 const char *str
= stun_strerror (code
);
510 size_t len
= strlen (str
);
511 div_t d
= div (code
, 100);
513 uint8_t *ptr
= stun_message_append (msg
, STUN_ATTRIBUTE_ERROR_CODE
, 4 + len
);
515 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE
;
520 memcpy (ptr
+ 4, str
, len
);
521 return STUN_MESSAGE_RETURN_SUCCESS
;
524 int stun_message_validate_buffer_length (const uint8_t *msg
, size_t length
)
531 stun_debug ("STUN error: No data!\n");
532 return STUN_MESSAGE_BUFFER_INVALID
;
537 stun_debug ("STUN error: RTP or other non-protocol packet!\n");
538 return STUN_MESSAGE_BUFFER_INVALID
; // RTP or other non-STUN packet
543 stun_debug ("STUN error: Incomplete STUN message header!\n");
544 return STUN_MESSAGE_BUFFER_INCOMPLETE
;
547 mlen
= stun_getw (msg
+ STUN_MESSAGE_LENGTH_POS
) +
548 STUN_MESSAGE_HEADER_LENGTH
;
550 if (stun_padding (mlen
))
552 stun_debug ("STUN error: Invalid message length: %u!\n", (unsigned)mlen
);
553 return STUN_MESSAGE_BUFFER_INVALID
; // wrong padding
558 stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n",
559 (unsigned)length
, (unsigned)mlen
);
560 return STUN_MESSAGE_BUFFER_INCOMPLETE
; // partial message
566 /* from then on, we know we have the entire packet in buffer */
569 size_t alen
= stun_align (stun_getw (msg
+ STUN_ATTRIBUTE_TYPE_LEN
));
571 /* thanks to padding check, if (end > msg) then there is not only one
572 * but at least 4 bytes left */
577 stun_debug ("STUN error: %u instead of %u bytes for attribute!\n",
578 (unsigned)len
, (unsigned)alen
);
579 return STUN_MESSAGE_BUFFER_INVALID
; // no room for attribute value + padding
590 * copies STUN message transaction ID
592 void stun_message_id (const StunMessage
*msg
, stun_transid_t id
)
594 memcpy (id
, msg
->buffer
+ STUN_MESSAGE_TRANS_ID_POS
, STUN_MESSAGE_TRANS_ID_LEN
);
598 * @return STUN message method (value from 0 to 0xfff)
600 stun_method_t
stun_message_get_method (const StunMessage
*msg
)
602 uint16_t t
= stun_getw (msg
->buffer
);
604 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
605 which states that 8th and 12th bits are for the class and that 0x01 is
607 So 0x0115 is reported as a "connect error response", while it should be
608 a data indication, which message type should actually be 0x0017
609 This should fix the issue, and it's considered safe since the "connect"
610 method doesn't exist anymore */
613 return (stun_method_t
)(((t
& 0x3e00) >> 2) | ((t
& 0x00e0) >> 1) |
619 * @return STUN message class in host byte order (value from 0 to 3)
621 stun_class_t
stun_message_get_class (const StunMessage
*msg
)
623 uint16_t t
= stun_getw (msg
->buffer
);
625 A google/msn data indication is 0x0115 which is contrary to the RFC 5389
626 which states that 8th and 12th bits are for the class and that 0x01 is
628 So 0x0115 is reported as a "connect error response", while it should be
629 a data indication, which message type should actually be 0x0017
630 This should fix the issue, and it's considered safe since the "connect"
631 method doesn't exist anymore */
634 return (stun_class_t
)(((t
& 0x0100) >> 7) | ((t
& 0x0010) >> 4));
638 * Checks if an attribute is present within a STUN message.
640 * @param msg valid STUN message
641 * @param type STUN attribute type (host byte order)
643 * @return whether there is a MESSAGE-INTEGRITY attribute
645 bool stun_message_has_attribute (const StunMessage
*msg
, stun_attr_type_t type
)
648 return stun_message_find (msg
, type
, &dummy
) != NULL
;