4cc339228cb5620c03ac8b1dc7335b3597ce49c5
[platform/upstream/libnice.git] / stun / stunmessage.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
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
8  *
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/
13  *
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
17  * License.
18  *
19  * The Original Code is the Nice GLib ICE library.
20  *
21  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22  * Corporation. All Rights Reserved.
23  *
24  * Contributors:
25  *   Youness Alaoui, Collabora Ltd.
26  *   Rémi Denis-Courmont, Nokia
27  *
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.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include <config.h>
41 #endif
42
43 #include "stunmessage.h"
44 #include "utils.h"
45
46 #ifdef _WIN32
47 #include <winsock2.h>
48 #include <ws2tcpip.h>
49 #else
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #endif
54
55
56 #include <string.h>
57 #include <stdlib.h>
58
59 bool stun_message_init (StunMessage *msg, StunClass c, StunMethod m,
60     const StunTransactionId id)
61 {
62
63   if (msg->buffer_len < STUN_MESSAGE_HEADER_LENGTH)
64     return FALSE;
65
66   memset (msg->buffer, 0, 4);
67   stun_set_type (msg->buffer, c, m);
68
69   memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS,
70       id, STUN_MESSAGE_TRANS_ID_LEN);
71
72   return TRUE;
73 }
74
75 uint16_t stun_message_length (const StunMessage *msg)
76 {
77   return stun_getw (msg->buffer + STUN_MESSAGE_LENGTH_POS) +
78       STUN_MESSAGE_HEADER_LENGTH;
79 }
80
81
82
83
84 const void *
85 stun_message_find (const StunMessage *msg, StunAttribute type,
86     uint16_t *palen)
87 {
88   size_t length = stun_message_length (msg);
89   size_t offset = 0;
90
91   /* In MS-TURN, IDs of REALM and NONCE STUN attributes are swapped. */
92   if (msg->agent && msg->agent->compatibility == STUN_COMPATIBILITY_OC2007)
93   {
94     if (type == STUN_ATTRIBUTE_REALM)
95       type = STUN_ATTRIBUTE_NONCE;
96     else if (type == STUN_ATTRIBUTE_NONCE)
97       type = STUN_ATTRIBUTE_REALM;
98   }
99
100   offset = STUN_MESSAGE_ATTRIBUTES_POS;
101
102   while (offset < length)
103   {
104     uint16_t atype = stun_getw (msg->buffer + offset);
105     size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN);
106
107
108     offset += STUN_ATTRIBUTE_VALUE_POS;
109
110     if (atype == type)
111     {
112       *palen = alen;
113       return msg->buffer + offset;
114     }
115
116     /* Look for and ignore misordered attributes */
117     switch (atype)
118     {
119       case STUN_ATTRIBUTE_MESSAGE_INTEGRITY:
120         /* Only fingerprint may come after M-I */
121         if (type == STUN_ATTRIBUTE_FINGERPRINT)
122           break;
123         return NULL;
124
125       case STUN_ATTRIBUTE_FINGERPRINT:
126         /* Nothing may come after FPR */
127         return NULL;
128
129       default:
130         /* Nothing misordered. */
131         break;
132     }
133
134     if (!(msg->agent &&
135             (msg->agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES)))
136       alen = stun_align (alen);
137
138     offset += alen;
139   }
140
141   return NULL;
142 }
143
144
145 StunMessageReturn
146 stun_message_find_flag (const StunMessage *msg, StunAttribute type)
147 {
148   const void *ptr;
149   uint16_t len = 0;
150
151   ptr = stun_message_find (msg, type, &len);
152   if (ptr == NULL)
153     return STUN_MESSAGE_RETURN_NOT_FOUND;
154   return (len == 0) ? STUN_MESSAGE_RETURN_SUCCESS :
155       STUN_MESSAGE_RETURN_INVALID;
156 }
157
158
159 StunMessageReturn
160 stun_message_find32 (const StunMessage *msg, StunAttribute type,
161     uint32_t *pval)
162 {
163   const void *ptr;
164   uint16_t len = 0;
165
166   ptr = stun_message_find (msg, type, &len);
167   if (ptr == NULL)
168     return STUN_MESSAGE_RETURN_NOT_FOUND;
169
170   if (len == 4)
171   {
172     uint32_t val;
173
174     memcpy (&val, ptr, sizeof (val));
175     *pval = ntohl (val);
176     return STUN_MESSAGE_RETURN_SUCCESS;
177   }
178   return STUN_MESSAGE_RETURN_INVALID;
179 }
180
181
182 StunMessageReturn
183 stun_message_find64 (const StunMessage *msg, StunAttribute type,
184     uint64_t *pval)
185 {
186   const void *ptr;
187   uint16_t len = 0;
188
189   ptr = stun_message_find (msg, type, &len);
190   if (ptr == NULL)
191     return STUN_MESSAGE_RETURN_NOT_FOUND;
192
193   if (len == 8)
194   {
195     uint32_t tab[2];
196
197     memcpy (tab, ptr, sizeof (tab));
198     *pval = ((uint64_t)ntohl (tab[0]) << 32) | ntohl (tab[1]);
199     return STUN_MESSAGE_RETURN_SUCCESS;
200   }
201   return STUN_MESSAGE_RETURN_INVALID;
202 }
203
204
205 StunMessageReturn
206 stun_message_find_string (const StunMessage *msg, StunAttribute type,
207     char *buf, size_t buflen)
208 {
209   const unsigned char *ptr;
210   uint16_t len = 0;
211
212   ptr = stun_message_find (msg, type, &len);
213   if (ptr == NULL)
214     return STUN_MESSAGE_RETURN_NOT_FOUND;
215
216   if (len >= buflen)
217     return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
218
219   memcpy (buf, ptr, len);
220   buf[len] = '\0';
221   return STUN_MESSAGE_RETURN_SUCCESS;
222 }
223
224
225 StunMessageReturn
226 stun_message_find_addr (const StunMessage *msg, StunAttribute type,
227     struct sockaddr_storage *addr, socklen_t *addrlen)
228 {
229   const uint8_t *ptr;
230   uint16_t len = 0;
231
232   ptr = stun_message_find (msg, type, &len);
233   if (ptr == NULL)
234     return STUN_MESSAGE_RETURN_NOT_FOUND;
235
236   if (len < 4)
237     return STUN_MESSAGE_RETURN_INVALID;
238
239   switch (ptr[1])
240   {
241     case 1:
242       {
243         struct sockaddr_in *ip4 = (struct sockaddr_in *)addr;
244         if (((size_t) *addrlen < sizeof (*ip4)) || (len != 8))
245         {
246           *addrlen = sizeof (*ip4);
247           return STUN_MESSAGE_RETURN_INVALID;
248         }
249
250         memset (ip4, 0, *addrlen);
251         ip4->sin_family = AF_INET;
252 #ifdef HAVE_SA_LEN
253         ip4->sin_len =
254 #endif
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;
259       }
260
261     case 2:
262       {
263         struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)addr;
264         if (((size_t) *addrlen < sizeof (*ip6)) || (len != 20))
265         {
266           *addrlen = sizeof (*ip6);
267           return STUN_MESSAGE_RETURN_INVALID;
268         }
269
270         memset (ip6, 0, *addrlen);
271         ip6->sin6_family = AF_INET6;
272 #ifdef HAVE_SA_LEN
273         ip6->sin6_len =
274 #endif
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;
279       }
280
281     default:
282       return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
283   }
284 }
285
286 StunMessageReturn
287 stun_message_find_xor_addr (const StunMessage *msg, StunAttribute type,
288     struct sockaddr_storage *addr, socklen_t *addrlen)
289 {
290   StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
291   if (val)
292     return val;
293
294   return stun_xor_address (msg, addr, *addrlen, STUN_MAGIC_COOKIE);
295 }
296
297 StunMessageReturn
298 stun_message_find_xor_addr_full (const StunMessage *msg, StunAttribute type,
299     struct sockaddr_storage *addr, socklen_t *addrlen, uint32_t magic_cookie)
300 {
301   StunMessageReturn val = stun_message_find_addr (msg, type, addr, addrlen);
302   if (val)
303     return val;
304
305   return stun_xor_address (msg, addr, *addrlen, magic_cookie);
306 }
307
308 StunMessageReturn
309 stun_message_find_error (const StunMessage *msg, int *code)
310 {
311   uint16_t alen = 0;
312   const uint8_t *ptr = stun_message_find (msg, STUN_ATTRIBUTE_ERROR_CODE, &alen);
313   uint8_t class, number;
314
315   if (ptr == NULL)
316     return STUN_MESSAGE_RETURN_NOT_FOUND;
317   if (alen < 4)
318     return STUN_MESSAGE_RETURN_INVALID;
319
320   class = ptr[2] & 0x7;
321   number = ptr[3];
322   if ((class < 3) || (class > 6) || (number > 99))
323     return STUN_MESSAGE_RETURN_INVALID;
324
325   *code = (class * 100) + number;
326   return STUN_MESSAGE_RETURN_SUCCESS;
327 }
328
329 void *
330 stun_message_append (StunMessage *msg, StunAttribute type, size_t length)
331 {
332   uint8_t *a;
333   uint16_t mlen = stun_message_length (msg);
334
335   /* In MS-TURN, IDs of REALM and NONCE STUN attributes are swapped. */
336   if (msg->agent && msg->agent->compatibility == STUN_COMPATIBILITY_OC2007)
337   {
338     if (type == STUN_ATTRIBUTE_NONCE)
339       type = STUN_ATTRIBUTE_REALM;
340     else if (type == STUN_ATTRIBUTE_REALM)
341       type = STUN_ATTRIBUTE_NONCE;
342   }
343
344   if ((size_t)mlen + STUN_ATTRIBUTE_HEADER_LENGTH + length > msg->buffer_len)
345     return NULL;
346
347
348   a = msg->buffer + mlen;
349   a = stun_setw (a, type);
350   if (msg->agent &&
351       (msg->agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES))
352   {
353     a = stun_setw (a, length);
354   } else {
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));
358
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);
363     }
364   }
365
366   mlen +=  4 + length;
367
368   stun_setw (msg->buffer + STUN_MESSAGE_LENGTH_POS, mlen - STUN_MESSAGE_HEADER_LENGTH);
369   return a;
370 }
371
372
373 StunMessageReturn
374 stun_message_append_bytes (StunMessage *msg, StunAttribute type,
375     const void *data, size_t len)
376 {
377   void *ptr = stun_message_append (msg, type, len);
378   if (ptr == NULL)
379     return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
380
381   if (len > 0)
382     memcpy (ptr, data, len);
383
384   return STUN_MESSAGE_RETURN_SUCCESS;
385 }
386
387
388 StunMessageReturn
389 stun_message_append_flag (StunMessage *msg, StunAttribute type)
390 {
391   return stun_message_append_bytes (msg, type, NULL, 0);
392 }
393
394
395 StunMessageReturn
396 stun_message_append32 (StunMessage *msg, StunAttribute type,
397     uint32_t value)
398 {
399   value = htonl (value);
400   return stun_message_append_bytes (msg, type, &value, 4);
401 }
402
403
404 StunMessageReturn
405 stun_message_append64 (StunMessage *msg, StunAttribute type,
406     uint64_t value)
407 {
408   uint32_t tab[2];
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);
412 }
413
414
415 StunMessageReturn
416 stun_message_append_string (StunMessage * msg, StunAttribute type,
417     const char *str)
418 {
419   return stun_message_append_bytes (msg, type, str, strlen (str));
420 }
421
422 StunMessageReturn
423 stun_message_append_addr (StunMessage *msg, StunAttribute type,
424     const struct sockaddr *addr, socklen_t addrlen)
425 {
426   const void *pa;
427   uint8_t *ptr;
428   uint16_t alen, port;
429   uint8_t family;
430
431   union {
432     const struct sockaddr *addr;
433     const struct sockaddr_in *in;
434     const struct sockaddr_in6 *in6;
435   } sa;
436
437   if ((size_t) addrlen < sizeof (struct sockaddr))
438     return STUN_MESSAGE_RETURN_INVALID;
439
440   sa.addr = addr;
441
442   switch (addr->sa_family)
443   {
444     case AF_INET:
445       {
446         const struct sockaddr_in *ip4 = sa.in;
447         family = 1;
448         port = ip4->sin_port;
449         alen = 4;
450         pa = &ip4->sin_addr;
451         break;
452       }
453
454     case AF_INET6:
455       {
456         const struct sockaddr_in6 *ip6 = sa.in6;
457         if ((size_t) addrlen < sizeof (*ip6))
458           return STUN_MESSAGE_RETURN_INVALID;
459
460         family = 2;
461         port = ip6->sin6_port;
462         alen = 16;
463         pa = &ip6->sin6_addr;
464         break;
465       }
466
467     default:
468       return STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS;
469   }
470
471   ptr = stun_message_append (msg, type, 4 + alen);
472   if (ptr == NULL)
473     return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
474
475   ptr[0] = 0;
476   ptr[1] = family;
477   memcpy (ptr + 2, &port, 2);
478   memcpy (ptr + 4, pa, alen);
479   return STUN_MESSAGE_RETURN_SUCCESS;
480 }
481
482
483 StunMessageReturn
484 stun_message_append_xor_addr (StunMessage *msg, StunAttribute type,
485     const struct sockaddr_storage *addr, socklen_t addrlen)
486 {
487   StunMessageReturn val;
488   /* Must be big enough to hold any supported address: */
489   struct sockaddr_storage tmpaddr;
490
491   if ((size_t) addrlen > sizeof (tmpaddr))
492     addrlen = sizeof (tmpaddr);
493   memcpy (&tmpaddr, addr, addrlen);
494
495   val = stun_xor_address (msg, &tmpaddr, addrlen,
496       STUN_MAGIC_COOKIE);
497   if (val)
498     return val;
499
500   return stun_message_append_addr (msg, type, (struct sockaddr *) &tmpaddr,
501       addrlen);
502 }
503
504 StunMessageReturn
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)
508 {
509   StunMessageReturn val;
510   /* Must be big enough to hold any supported address: */
511   struct sockaddr_storage tmpaddr;
512
513   if ((size_t) addrlen > sizeof (tmpaddr))
514     addrlen = sizeof (tmpaddr);
515   memcpy (&tmpaddr, addr, addrlen);
516
517   val = stun_xor_address (msg, &tmpaddr, addrlen, magic_cookie);
518   if (val)
519     return val;
520
521   return stun_message_append_addr (msg, type, (struct sockaddr *) &tmpaddr,
522       addrlen);
523 }
524
525
526
527 StunMessageReturn
528 stun_message_append_error (StunMessage *msg, StunError code)
529 {
530   const char *str = stun_strerror (code);
531   size_t len = strlen (str);
532
533   uint8_t *ptr = stun_message_append (msg, STUN_ATTRIBUTE_ERROR_CODE, 4 + len);
534   if (ptr == NULL)
535     return STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE;
536
537   memset (ptr, 0, 2);
538   ptr[2] = code / 100;
539   ptr[3] = code % 100;
540   memcpy (ptr + 4, str, len);
541   return STUN_MESSAGE_RETURN_SUCCESS;
542 }
543
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)
551 {
552   size_t mlen;
553
554   if (total_length < 1 || n_buffers == 0 || buffers[0].buffer == NULL)
555   {
556     stun_debug ("STUN error: No data!");
557     return STUN_MESSAGE_BUFFER_INVALID;
558   }
559
560   if (buffers[0].buffer[0] >> 6)
561   {
562     return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
563   }
564
565   if (total_length < STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN)
566   {
567     stun_debug ("STUN error: Incomplete STUN message header!");
568     return STUN_MESSAGE_BUFFER_INCOMPLETE;
569   }
570
571   if (buffers[0].size >= STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN) {
572     /* Fast path. */
573     mlen = stun_getw (buffers[0].buffer + STUN_MESSAGE_LENGTH_POS);
574   } else {
575     /* Slow path. Tiny buffers abound. */
576     size_t skip_remaining = STUN_MESSAGE_LENGTH_POS;
577     unsigned int i;
578
579     /* Skip bytes. */
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;
584       else
585         break;
586     }
587
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);
592     } else {
593       mlen = (*(buffers[i].buffer + skip_remaining) << 8) |
594              (*(buffers[i + 1].buffer));
595     }
596   }
597
598   mlen += STUN_MESSAGE_HEADER_LENGTH;
599
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
603   }
604
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
609   }
610
611   return mlen;
612 }
613
614 int stun_message_validate_buffer_length (const uint8_t *msg, size_t length,
615     bool has_padding)
616 {
617   ssize_t fast_retval;
618   size_t mlen;
619   size_t len;
620   StunInputVector input_buffer = { msg, length };
621
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)
626     return fast_retval;
627
628   mlen = fast_retval;
629
630   /* Skip past the header (validated above). */
631   msg += 20;
632   len = mlen - 20;
633
634   /* from then on, we know we have the entire packet in buffer */
635   while (len > 0)
636   {
637     size_t alen;
638
639     if (len < 4)
640     {
641       stun_debug ("STUN error: Incomplete STUN attribute header of length "
642           "%u bytes!", (unsigned)len);
643       return STUN_MESSAGE_BUFFER_INVALID;
644     }
645
646     alen = stun_getw (msg + STUN_ATTRIBUTE_TYPE_LEN);
647     if (has_padding)
648       alen = stun_align (alen);
649
650     /* thanks to padding check, if (end > msg) then there is not only one
651      * but at least 4 bytes left */
652     len -= 4;
653
654     if (len < alen)
655     {
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
659     }
660
661     len -= alen;
662     msg += 4 + alen;
663   }
664
665   return mlen;
666 }
667
668 void stun_message_id (const StunMessage *msg, StunTransactionId id)
669 {
670   memcpy (id, msg->buffer + STUN_MESSAGE_TRANS_ID_POS, STUN_MESSAGE_TRANS_ID_LEN);
671 }
672
673 StunMethod stun_message_get_method (const StunMessage *msg)
674 {
675   uint16_t t = stun_getw (msg->buffer);
676   /* HACK HACK HACK
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
679      for indications...
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 */
684   if (t == 0x0115)
685     t = 0x0017;
686   return (StunMethod)(((t & 0x3e00) >> 2) | ((t & 0x00e0) >> 1) |
687                           (t & 0x000f));
688 }
689
690
691 StunClass stun_message_get_class (const StunMessage *msg)
692 {
693   uint16_t t = stun_getw (msg->buffer);
694   /* HACK HACK HACK
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
697      for indications...
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 */
702   if (t == 0x0115)
703     t = 0x0017;
704   return (StunClass)(((t & 0x0100) >> 7) | ((t & 0x0010) >> 4));
705 }
706
707 bool stun_message_has_attribute (const StunMessage *msg, StunAttribute type)
708 {
709   uint16_t dummy;
710   return stun_message_find (msg, type, &dummy) != NULL;
711 }
712
713
714 bool stun_optional (uint16_t t)
715 {
716   return (t >> 15) == 1;
717 }
718
719 const char *stun_strerror (StunError code)
720 {
721   static const struct
722   {
723     StunError code;
724     char     phrase[32];
725   } tab[] =
726   {
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" },
745   };
746   const char *str = "Unknown error";
747   size_t i;
748
749   for (i = 0; i < (sizeof (tab) / sizeof (tab[0])); i++)
750   {
751     if (tab[i].code == code)
752     {
753       str = tab[i].phrase;
754       break;
755     }
756   }
757
758   /* Maximum allowed error message length */
759   //  assert (strlen (str) < 128);
760   return str;
761 }