Find and document every message attribute/error code and from which RFC/Draft they...
[sipe-libnice.git] / stun / stunmessage.c
blobf6b60fe09799f683af0666da2857d72c551e4407
1 /*
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
15 * License.
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.
22 * Contributors:
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.
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
40 #include "stunmessage.h"
42 #ifdef _WIN32
43 #include <winsock2.h>
44 #include <ws2tcpip.h>
45 #else
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #endif
51 #include <string.h>
52 #include <stdlib.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)
59 return FALSE;
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);
67 return TRUE;
70 uint16_t stun_message_length (const StunMessage *msg)
72 return stun_getw (msg->buffer + STUN_MESSAGE_LENGTH_POS) +
73 STUN_MESSAGE_HEADER_LENGTH;
79 const void *
80 stun_message_find (const StunMessage *msg, stun_attr_type_t type,
81 uint16_t *palen)
83 size_t length = stun_message_length (msg);
84 size_t offset = 0;
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;
97 if (atype == type)
99 *palen = alen;
100 return msg->buffer + offset;
103 /* Look for and ignore misordered attributes */
104 switch (atype)
106 case STUN_ATTRIBUTE_MESSAGE_INTEGRITY:
107 /* Only fingerprint may come after M-I */
108 if (type == STUN_ATTRIBUTE_FINGERPRINT)
109 break;
111 case STUN_ATTRIBUTE_FINGERPRINT:
112 /* Nothing may come after FPR */
113 return NULL;
116 alen = stun_align (alen);
117 offset += alen;
120 return NULL;
124 StunMessageReturn
125 stun_message_find_flag (const StunMessage *msg, stun_attr_type_t type)
127 const void *ptr;
128 uint16_t len;
130 ptr = stun_message_find (msg, type, &len);
131 if (ptr == NULL)
132 return STUN_MESSAGE_RETURN_NOT_FOUND;
133 return (len == 0) ? STUN_MESSAGE_RETURN_SUCCESS :
134 STUN_MESSAGE_RETURN_INVALID;
138 StunMessageReturn
139 stun_message_find32 (const StunMessage *msg, stun_attr_type_t type,
140 uint32_t *pval)
142 const void *ptr;
143 uint16_t len;
145 ptr = stun_message_find (msg, type, &len);
146 if (ptr == NULL)
147 return STUN_MESSAGE_RETURN_NOT_FOUND;
149 if (len == 4)
151 uint32_t val;
153 memcpy (&val, ptr, sizeof (val));
154 *pval = ntohl (val);
155 return STUN_MESSAGE_RETURN_SUCCESS;
157 return STUN_MESSAGE_RETURN_INVALID;
161 StunMessageReturn
162 stun_message_find64 (const StunMessage *msg, stun_attr_type_t type,
163 uint64_t *pval)
165 const void *ptr;
166 uint16_t len;
168 ptr = stun_message_find (msg, type, &len);
169 if (ptr == NULL)
170 return STUN_MESSAGE_RETURN_NOT_FOUND;
172 if (len == 8)
174 uint32_t tab[2];
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;
184 StunMessageReturn
185 stun_message_find_string (const StunMessage *msg, stun_attr_type_t type,
186 char *buf, size_t buflen)
188 const unsigned char *ptr;
189 uint16_t len;
191 ptr = stun_message_find (msg, type, &len);
192 if (ptr == NULL)
193 return STUN_MESSAGE_RETURN_NOT_FOUND;
195 if (len >= buflen)
196 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
198 memcpy (buf, ptr, len);
199 buf[len] = '\0';
200 return STUN_MESSAGE_RETURN_SUCCESS;
204 StunMessageReturn
205 stun_message_find_addr (const StunMessage *msg, stun_attr_type_t type,
206 struct sockaddr *addr, socklen_t *addrlen)
208 const uint8_t *ptr;
209 uint16_t len;
211 ptr = stun_message_find (msg, type, &len);
212 if (ptr == NULL)
213 return STUN_MESSAGE_RETURN_NOT_FOUND;
215 if (len < 4)
216 return STUN_MESSAGE_RETURN_INVALID;
218 switch (ptr[1])
220 case 1:
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;
231 #ifdef HAVE_SA_LEN
232 ip4->sin_len =
233 #endif
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;
240 case 2:
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;
251 #ifdef HAVE_SA_LEN
252 ip6->sin6_len =
253 #endif
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;
264 StunMessageReturn
265 stun_message_find_xor_addr (const StunMessage *msg, stun_attr_type_t type,
266 struct sockaddr *addr,
267 socklen_t *addrlen)
269 StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
270 if (val)
271 return val;
273 return stun_xor_address (msg, addr, *addrlen, STUN_MAGIC_COOKIE);
276 StunMessageReturn
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);
282 if (val)
283 return val;
285 return stun_xor_address (msg, addr, *addrlen, magic_cookie);
288 StunMessageReturn
289 stun_message_find_error (const StunMessage *msg, int *code)
291 uint16_t alen;
292 const uint8_t *ptr = stun_message_find (msg, STUN_ATTRIBUTE_ERROR_CODE, &alen);
293 uint8_t class, number;
295 if (ptr == NULL)
296 return STUN_MESSAGE_RETURN_NOT_FOUND;
297 if (alen < 4)
298 return STUN_MESSAGE_RETURN_INVALID;
300 class = ptr[2] & 0x7;
301 number = ptr[3];
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
318 * 32-bits boundary.
320 void *
321 stun_message_append (StunMessage *msg, stun_attr_type_t type, size_t length)
323 uint8_t *a;
324 uint16_t mlen = stun_message_length (msg);
326 if ((size_t)mlen + STUN_ATTRIBUTE_HEADER_LENGTH + length > msg->buffer_len)
327 return NULL;
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));
336 mlen += 4 + 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);
342 return a;
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.
355 StunMessageReturn
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);
360 if (ptr == NULL)
361 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
363 memcpy (ptr, data, len);
364 return STUN_MESSAGE_RETURN_SUCCESS;
368 StunMessageReturn
369 stun_message_append_flag (StunMessage *msg, stun_attr_type_t type)
371 return stun_message_append_bytes (msg, type, NULL, 0);
375 StunMessageReturn
376 stun_message_append32 (StunMessage *msg, stun_attr_type_t type,
377 uint32_t value)
379 value = htonl (value);
380 return stun_message_append_bytes (msg, type, &value, 4);
384 StunMessageReturn
385 stun_message_append64 (StunMessage *msg, stun_attr_type_t type,
386 uint64_t value)
388 uint32_t tab[2];
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);
395 StunMessageReturn
396 stun_message_append_string (StunMessage * msg, stun_attr_type_t type,
397 const char *str)
399 return stun_message_append_bytes (msg, type, str, strlen (str));
402 StunMessageReturn
403 stun_message_append_addr (StunMessage *msg, stun_attr_type_t type,
404 const struct sockaddr *addr, socklen_t addrlen)
406 const void *pa;
407 uint8_t *ptr;
408 uint16_t alen, port;
409 uint8_t family;
411 if ((size_t) addrlen < sizeof (struct sockaddr))
412 return STUN_MESSAGE_RETURN_INVALID;
414 switch (addr->sa_family)
416 case AF_INET:
418 const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
419 family = 1;
420 port = ip4->sin_port;
421 alen = 4;
422 pa = &ip4->sin_addr;
423 break;
426 case AF_INET6:
428 const struct sockaddr_in6 *ip6 = (const struct sockaddr_in6 *)addr;
429 if ((size_t) addrlen < sizeof (*ip6))
430 return STUN_MESSAGE_RETURN_INVALID;
432 family = 2;
433 port = ip6->sin6_port;
434 alen = 16;
435 pa = &ip6->sin6_addr;
436 break;
439 default:
440 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
443 ptr = stun_message_append (msg, type, 4 + alen);
444 if (ptr == NULL)
445 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
447 ptr[0] = 0;
448 ptr[1] = family;
449 memcpy (ptr + 2, &port, 2);
450 memcpy (ptr + 4, pa, alen);
451 return STUN_MESSAGE_RETURN_SUCCESS;
455 StunMessageReturn
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,
468 STUN_MAGIC_COOKIE);
469 if (val)
470 return val;
472 return stun_message_append_addr (msg, type, (struct sockaddr *)&xor,
473 addrlen);
476 StunMessageReturn
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);
490 if (val)
491 return val;
493 return stun_message_append_addr (msg, type, (struct sockaddr *)&xor,
494 addrlen);
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
506 StunMessageReturn
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);
514 if (ptr == NULL)
515 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
517 memset (ptr, 0, 2);
518 ptr[2] = d.quot;
519 ptr[3] = d.rem;
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)
526 size_t mlen;
527 size_t len;
529 if (length < 1)
531 stun_debug ("STUN error: No data!\n");
532 return STUN_MESSAGE_BUFFER_INVALID;
535 if (msg[0] >> 6)
537 stun_debug ("STUN error: RTP or other non-protocol packet!\n");
538 return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
541 if (length < 4)
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
556 if (length < mlen)
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
563 msg += 20;
564 len = mlen - 20;
566 /* from then on, we know we have the entire packet in buffer */
567 while (len > 0)
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 */
573 len -= 4;
575 if (len < alen)
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
582 len -= alen;
583 msg += 4 + alen;
586 return mlen;
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);
603 /* HACK HACK HACK
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
606 for indications...
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 */
611 if (t == 0x0115)
612 t = 0x0017;
613 return (stun_method_t)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
614 (t & 0x000f));
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);
624 /* HACK HACK HACK
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
627 for indications...
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 */
632 if (t == 0x0115)
633 t = 0x0017;
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)
647 uint16_t dummy;
648 return stun_message_find (msg, type, &dummy) != NULL;