Adding new stun API stun_agent_forget_transaction to forget a saved transaction id...
[sipe-libnice.git] / stun / stunmessage.c
blobd97ae2d26faf7536c62bbad089a41734e1382377
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"
41 #include "utils.h"
43 #ifdef _WIN32
44 #include <winsock2.h>
45 #include <ws2tcpip.h>
46 #else
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #endif
52 #include <string.h>
53 #include <stdlib.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)
60 return FALSE;
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);
68 return TRUE;
71 uint16_t stun_message_length (const StunMessage *msg)
73 return stun_getw (msg->buffer + STUN_MESSAGE_LENGTH_POS) +
74 STUN_MESSAGE_HEADER_LENGTH;
80 const void *
81 stun_message_find (const StunMessage *msg, StunAttribute type,
82 uint16_t *palen)
84 size_t length = stun_message_length (msg);
85 size_t offset = 0;
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;
98 if (atype == type)
100 *palen = alen;
101 return msg->buffer + offset;
104 /* Look for and ignore misordered attributes */
105 switch (atype)
107 case STUN_ATTRIBUTE_MESSAGE_INTEGRITY:
108 /* Only fingerprint may come after M-I */
109 if (type == STUN_ATTRIBUTE_FINGERPRINT)
110 break;
112 case STUN_ATTRIBUTE_FINGERPRINT:
113 /* Nothing may come after FPR */
114 return NULL;
117 alen = stun_align (alen);
118 offset += alen;
121 return NULL;
125 StunMessageReturn
126 stun_message_find_flag (const StunMessage *msg, StunAttribute type)
128 const void *ptr;
129 uint16_t len;
131 ptr = stun_message_find (msg, type, &len);
132 if (ptr == NULL)
133 return STUN_MESSAGE_RETURN_NOT_FOUND;
134 return (len == 0) ? STUN_MESSAGE_RETURN_SUCCESS :
135 STUN_MESSAGE_RETURN_INVALID;
139 StunMessageReturn
140 stun_message_find32 (const StunMessage *msg, StunAttribute type,
141 uint32_t *pval)
143 const void *ptr;
144 uint16_t len;
146 ptr = stun_message_find (msg, type, &len);
147 if (ptr == NULL)
148 return STUN_MESSAGE_RETURN_NOT_FOUND;
150 if (len == 4)
152 uint32_t val;
154 memcpy (&val, ptr, sizeof (val));
155 *pval = ntohl (val);
156 return STUN_MESSAGE_RETURN_SUCCESS;
158 return STUN_MESSAGE_RETURN_INVALID;
162 StunMessageReturn
163 stun_message_find64 (const StunMessage *msg, StunAttribute type,
164 uint64_t *pval)
166 const void *ptr;
167 uint16_t len;
169 ptr = stun_message_find (msg, type, &len);
170 if (ptr == NULL)
171 return STUN_MESSAGE_RETURN_NOT_FOUND;
173 if (len == 8)
175 uint32_t tab[2];
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;
185 StunMessageReturn
186 stun_message_find_string (const StunMessage *msg, StunAttribute type,
187 char *buf, size_t buflen)
189 const unsigned char *ptr;
190 uint16_t len;
192 ptr = stun_message_find (msg, type, &len);
193 if (ptr == NULL)
194 return STUN_MESSAGE_RETURN_NOT_FOUND;
196 if (len >= buflen)
197 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
199 memcpy (buf, ptr, len);
200 buf[len] = '\0';
201 return STUN_MESSAGE_RETURN_SUCCESS;
205 StunMessageReturn
206 stun_message_find_addr (const StunMessage *msg, StunAttribute type,
207 struct sockaddr *addr, socklen_t *addrlen)
209 const uint8_t *ptr;
210 uint16_t len;
212 ptr = stun_message_find (msg, type, &len);
213 if (ptr == NULL)
214 return STUN_MESSAGE_RETURN_NOT_FOUND;
216 if (len < 4)
217 return STUN_MESSAGE_RETURN_INVALID;
219 switch (ptr[1])
221 case 1:
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;
232 #ifdef HAVE_SA_LEN
233 ip4->sin_len =
234 #endif
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;
241 case 2:
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;
252 #ifdef HAVE_SA_LEN
253 ip6->sin6_len =
254 #endif
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;
265 StunMessageReturn
266 stun_message_find_xor_addr (const StunMessage *msg, StunAttribute type,
267 struct sockaddr *addr,
268 socklen_t *addrlen)
270 StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
271 if (val)
272 return val;
274 return stun_xor_address (msg, addr, *addrlen, STUN_MAGIC_COOKIE);
277 StunMessageReturn
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);
283 if (val)
284 return val;
286 return stun_xor_address (msg, addr, *addrlen, magic_cookie);
289 StunMessageReturn
290 stun_message_find_error (const StunMessage *msg, int *code)
292 uint16_t alen;
293 const uint8_t *ptr = stun_message_find (msg, STUN_ATTRIBUTE_ERROR_CODE, &alen);
294 uint8_t class, number;
296 if (ptr == NULL)
297 return STUN_MESSAGE_RETURN_NOT_FOUND;
298 if (alen < 4)
299 return STUN_MESSAGE_RETURN_INVALID;
301 class = ptr[2] & 0x7;
302 number = ptr[3];
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;
310 void *
311 stun_message_append (StunMessage *msg, StunAttribute type, size_t length)
313 uint8_t *a;
314 uint16_t mlen = stun_message_length (msg);
316 if ((size_t)mlen + STUN_ATTRIBUTE_HEADER_LENGTH + length > msg->buffer_len)
317 return NULL;
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));
326 mlen += 4 + 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);
332 return a;
336 StunMessageReturn
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);
341 if (ptr == NULL)
342 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
344 memcpy (ptr, data, len);
345 return STUN_MESSAGE_RETURN_SUCCESS;
349 StunMessageReturn
350 stun_message_append_flag (StunMessage *msg, StunAttribute type)
352 return stun_message_append_bytes (msg, type, NULL, 0);
356 StunMessageReturn
357 stun_message_append32 (StunMessage *msg, StunAttribute type,
358 uint32_t value)
360 value = htonl (value);
361 return stun_message_append_bytes (msg, type, &value, 4);
365 StunMessageReturn
366 stun_message_append64 (StunMessage *msg, StunAttribute type,
367 uint64_t value)
369 uint32_t tab[2];
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);
376 StunMessageReturn
377 stun_message_append_string (StunMessage * msg, StunAttribute type,
378 const char *str)
380 return stun_message_append_bytes (msg, type, str, strlen (str));
383 StunMessageReturn
384 stun_message_append_addr (StunMessage *msg, StunAttribute type,
385 const struct sockaddr *addr, socklen_t addrlen)
387 const void *pa;
388 uint8_t *ptr;
389 uint16_t alen, port;
390 uint8_t family;
392 if ((size_t) addrlen < sizeof (struct sockaddr))
393 return STUN_MESSAGE_RETURN_INVALID;
395 switch (addr->sa_family)
397 case AF_INET:
399 const struct sockaddr_in *ip4 = (const struct sockaddr_in *)addr;
400 family = 1;
401 port = ip4->sin_port;
402 alen = 4;
403 pa = &ip4->sin_addr;
404 break;
407 case AF_INET6:
409 const struct sockaddr_in6 *ip6 = (const struct sockaddr_in6 *)addr;
410 if ((size_t) addrlen < sizeof (*ip6))
411 return STUN_MESSAGE_RETURN_INVALID;
413 family = 2;
414 port = ip6->sin6_port;
415 alen = 16;
416 pa = &ip6->sin6_addr;
417 break;
420 default:
421 return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
424 ptr = stun_message_append (msg, type, 4 + alen);
425 if (ptr == NULL)
426 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
428 ptr[0] = 0;
429 ptr[1] = family;
430 memcpy (ptr + 2, &port, 2);
431 memcpy (ptr + 4, pa, alen);
432 return STUN_MESSAGE_RETURN_SUCCESS;
436 StunMessageReturn
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,
449 STUN_MAGIC_COOKIE);
450 if (val)
451 return val;
453 return stun_message_append_addr (msg, type, (struct sockaddr *)&xor,
454 addrlen);
457 StunMessageReturn
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);
471 if (val)
472 return val;
474 return stun_message_append_addr (msg, type, (struct sockaddr *)&xor,
475 addrlen);
480 StunMessageReturn
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);
488 if (ptr == NULL)
489 return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
491 memset (ptr, 0, 2);
492 ptr[2] = d.quot;
493 ptr[3] = d.rem;
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)
500 size_t mlen;
501 size_t len;
503 if (length < 1)
505 stun_debug ("STUN error: No data!\n");
506 return STUN_MESSAGE_BUFFER_INVALID;
509 if (msg[0] >> 6)
511 stun_debug ("STUN error: RTP or other non-protocol packet!\n");
512 return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
515 if (length < 4)
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
530 if (length < mlen)
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
537 msg += 20;
538 len = mlen - 20;
540 /* from then on, we know we have the entire packet in buffer */
541 while (len > 0)
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 */
547 len -= 4;
549 if (len < alen)
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
556 len -= alen;
557 msg += 4 + alen;
560 return mlen;
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);
571 /* HACK HACK HACK
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
574 for indications...
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 */
579 if (t == 0x0115)
580 t = 0x0017;
581 return (StunMethod)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
582 (t & 0x000f));
586 StunClass stun_message_get_class (const StunMessage *msg)
588 uint16_t t = stun_getw (msg->buffer);
589 /* HACK HACK HACK
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
592 for indications...
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 */
597 if (t == 0x0115)
598 t = 0x0017;
599 return (StunClass)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
602 bool stun_message_has_attribute (const StunMessage *msg, StunAttribute type)
604 uint16_t dummy;
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)
616 static const struct
618 StunError code;
619 char phrase[32];
620 } tab[] =
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";
642 size_t i;
644 for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
646 if (tab[i].code == code)
648 str = tab[i].phrase;
649 break;
653 /* Maximum allowed error message length */
654 // assert (strlen (str) < 128);
655 return str;