glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / nat / gnunet-service-nat_stun.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14 */
15 /**
16  * This code provides some support for doing STUN transactions.  We
17  * receive the simplest possible packet as the STUN server and try
18  * to respond properly.
19  *
20  * All STUN packets start with a simple header made of a type,
21  * length (excluding the header) and a 16-byte random transaction id.
22  * Following the header we may have zero or more attributes, each
23  * structured as a type, length and a value (whose format depends
24  * on the type, but often contains addresses).
25  * Of course all fields are in network format.
26  *
27  * This code was based on ministun.c.
28  *
29  * @file nat/gnunet-service-nat_stun.c
30  * @brief Functions for STUN functionality
31  * @author Bruno Souza Cabral
32  */
33
34 #include "platform.h"
35 #include "gnunet_util_lib.h"
36 #include "nat_stun.h"
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
39
40
41 /**
42  * Context for #stun_get_mapped(). 
43  * Used to store state across processing attributes.
44  */
45 struct StunState
46 {
47   uint16_t attr;
48 };
49
50
51 /**
52  * Extract the STUN_MAPPED_ADDRESS from the stun response.
53  * This is used as a callback for stun_handle_response
54  * when called from stun_request.
55  *
56  * @param[out] st pointer where we will set the type
57  * @param attr received stun attribute
58  * @param magic Magic cookie
59  * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port
60  * @return #GNUNET_OK if @a arg was initialized
61  */
62 static int
63 stun_get_mapped (struct StunState *st,
64                  const struct stun_attr *attr,
65                  uint32_t magic,
66                  struct sockaddr_in *arg)
67 {
68   const struct stun_addr *returned_addr;
69   struct sockaddr_in *sa = (struct sockaddr_in *) arg;
70   uint16_t type = ntohs (attr->attr);
71
72   switch (type)
73   {
74   case STUN_MAPPED_ADDRESS:
75     if ( (st->attr == STUN_XOR_MAPPED_ADDRESS) ||
76          (st->attr == STUN_MS_XOR_MAPPED_ADDRESS) )
77       return GNUNET_NO;
78     magic = 0;
79     break;
80   case STUN_MS_XOR_MAPPED_ADDRESS:
81     if (st->attr == STUN_XOR_MAPPED_ADDRESS)
82       return GNUNET_NO;
83     break;
84   case STUN_XOR_MAPPED_ADDRESS:
85     break;
86   default:
87     return GNUNET_NO;
88   }  
89   
90   if (ntohs (attr->len) < sizeof (struct stun_addr))
91     return GNUNET_NO;
92   returned_addr = (const struct stun_addr *)(attr + 1);
93   if (AF_INET != returned_addr->family)
94     return GNUNET_NO;
95   st->attr = type;
96   sa->sin_family = AF_INET;
97   sa->sin_port = returned_addr->port ^ htons (ntohl(magic) >> 16);
98   sa->sin_addr.s_addr = returned_addr->addr ^ magic;
99   return GNUNET_OK;
100 }
101
102
103 /**
104  * Handle an incoming STUN response.  Do some basic sanity checks on
105  * packet size and content, try to extract information.
106  * At the moment this only processes BIND requests,
107  * and returns the externally visible address of the original
108  * request.
109  *
110  * @param data the packet
111  * @param len the length of the packet in @a data
112  * @param[out] arg sockaddr_in where we will set our discovered address
113  * @return #GNUNET_OK on success,
114  *         #GNUNET_NO if the packet is invalid (not a stun packet)
115  */
116 int
117 GNUNET_NAT_stun_handle_packet_ (const void *data,
118                                 size_t len,
119                                 struct sockaddr_in *arg)
120 {
121   const struct stun_header *hdr;
122   const struct stun_attr *attr;
123   struct StunState st;
124   uint32_t advertised_message_size;
125   uint32_t message_magic_cookie;
126   int ret = GNUNET_SYSERR;
127
128   /* On entry, 'len' is the length of the UDP payload. After the
129    * initial checks it becomes the size of unprocessed options,
130    * while 'data' is advanced accordingly.
131    */
132   if (len < sizeof(struct stun_header))
133   {
134     LOG (GNUNET_ERROR_TYPE_DEBUG,
135          "Packet too short to be a STUN packet\n");
136     return GNUNET_NO;
137   }
138   hdr = data;
139   /* Skip header as it is already in hdr */
140   len -= sizeof(struct stun_header);
141   data += sizeof(struct stun_header);
142
143   /* len as advertised in the message */
144   advertised_message_size = ntohs (hdr->msglen);
145   message_magic_cookie = ntohl (hdr->magic);
146   /* Compare if the cookie match */
147   if (STUN_MAGIC_COOKIE != message_magic_cookie)
148   {
149     LOG (GNUNET_ERROR_TYPE_DEBUG,
150          "Invalid magic cookie for STUN packet\n");
151     return GNUNET_NO;
152   }
153
154   LOG (GNUNET_ERROR_TYPE_INFO,
155        "STUN Packet, msg %s (%04x), length: %d\n",
156        stun_msg2str (ntohs (hdr->msgtype)),
157        ntohs (hdr->msgtype),
158        advertised_message_size);
159   if (advertised_message_size > len)
160   {
161     LOG (GNUNET_ERROR_TYPE_INFO,
162          "Scrambled STUN packet length (got %d, expecting %d)\n",
163          advertised_message_size,
164          (int) len);
165     return GNUNET_NO;
166   }
167   len = advertised_message_size;
168   memset (&st, 0, sizeof(st));
169
170   while (len > 0)
171   {
172     if (len < sizeof (struct stun_attr))
173     {
174       LOG (GNUNET_ERROR_TYPE_INFO,
175            "Attribute too short (got %d, expecting %d)\n",
176            (int) len,
177            (int) sizeof (struct stun_attr));
178       break;
179     }
180     attr = (const struct stun_attr *) data;
181
182     /* compute total attribute length */
183     advertised_message_size = ntohs (attr->len) + sizeof (struct stun_attr);
184
185     /* Check if we still have space in our buffer */
186     if (advertised_message_size > len)
187     {
188       LOG (GNUNET_ERROR_TYPE_INFO,
189            "Inconsistent attribute (length %d exceeds remaining msg len %d)\n",
190            advertised_message_size,
191            (int) len);
192       break;
193     }
194     if (GNUNET_OK ==
195         stun_get_mapped (&st,
196                          attr,
197                          hdr->magic,
198                          arg))
199       ret = GNUNET_OK;
200     data += advertised_message_size;
201     len -= advertised_message_size;
202   }
203   return ret;
204 }
205
206 /* end of gnunet-service-nat_stun.c */