adding a first set of fixmes to stun code
[oweals/gnunet.git] / src / nat / nat_stun.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * 
23  * This code provides some support for doing STUN transactions.
24  * We send simplest possible packet ia REQUEST with BIND to a STUN server.
25  *
26  * All STUN packets start with a simple header made of a type,
27  * length (excluding the header) and a 16-byte random transaction id.
28  * Following the header we may have zero or more attributes, each
29  * structured as a type, length and a value (whose format depends
30  * on the type, but often contains addresses).
31  * Of course all fields are in network format.
32  * 
33  * This code was based on ministun.c.
34  *
35  *
36  * @file nat/nat_stun.c
37  * @brief Functions for STUN functionality
38  * @author Bruno Souza Cabral
39  */
40
41 #include "platform.h"
42 #include "gnunet_util_lib.h"
43 #include "gnunet_program_lib.h"
44 #include "gnunet_scheduler_lib.h"
45 #include "gnunet_nat_lib.h"
46
47
48 #include "nat_stun.h"
49
50 #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
51
52
53
54 /**
55  * Handle to a request given to the resolver.  Can be used to cancel
56  * the request prior to the timeout or successful execution.  Also
57  * used to track our internal state for the request.
58  */
59 struct GNUNET_NAT_StunRequestHandle {
60
61      /**
62      * Handle to a pending DNS lookup request.
63      */
64      struct GNUNET_RESOLVER_RequestHandle *dns_active;
65
66
67      /**
68      * Handle to the listen socket
69      */
70      struct GNUNET_NETWORK_Handle * sock;
71
72      /**
73      * Stun server address
74      */
75      char *stun_server ;
76
77      /**
78      * STUN port
79      */
80      int stun_port;
81
82 };
83
84
85
86 /* here we store credentials extracted from a message */
87 struct StunState {
88      uint16_t attr;
89 };
90
91
92 /**
93  * Convert a message to a StunClass
94  *
95  * @param msg the received message
96  * @return the converted StunClass
97  */
98 static int decode_class(int msg)
99 {
100      return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7);
101 }
102
103 /**
104  * Convert a message to a StunMethod
105  *
106  * @param msg the received message
107  * @return the converted StunMethod
108  */
109 static int decode_method(int msg)
110 {
111      return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2);
112 }
113
114 /**
115  * Encode a class and method to a compatible STUN format
116  *
117  * @param msg_class class to be converted
118  * @param method method to be converted
119  * @return message in a STUN compatible format
120  */
121 static int encode_message(StunClasses msg_class, StunMethods method)
122 {
123      return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) |
124             (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2);
125 }
126
127 /**
128  * Print a class and method from a STUN message
129  *
130  * @param msg
131  * @return string with the message class and method
132  */
133 static const char *stun_msg2str(int msg)
134 {
135
136      const struct { enum StunClasses value; const char *name; } classes[] = {
137              { STUN_REQUEST, "Request" },
138              { STUN_INDICATION, "Indication" },
139              { STUN_RESPONSE, "Response" },
140              { STUN_ERROR_RESPONSE, "Error Response" },
141              { 0, NULL }
142      };
143
144      const struct { enum StunMethods value; const char *name; } methods[] = {
145              { STUN_BINDING, "Binding" },
146              { 0, NULL }
147      };
148
149      static char result[32];
150      const char *msg_class = NULL;
151      const char *method = NULL;
152      int i;
153      int value;
154
155      value = decode_class(msg);
156      for (i = 0; classes[i].name; i++) {
157           msg_class = classes[i].name;
158           if (classes[i].value == value)
159                break;
160      }
161      value = decode_method(msg);
162      for (i = 0; methods[i].name; i++) {
163           method = methods[i].name;
164           if (methods[i].value == value)
165                break;
166      }
167      snprintf(result, sizeof(result), "%s %s",
168               method ? : "Unknown Method",
169               msg_class ? : "Unknown Class Message");
170      return result;
171 }
172
173 /**
174  * Print attribute name
175  *
176  * @param msg with a attribute type
177  * @return string with the attribute name
178  */
179 static const char *stun_attr2str(int msg)
180 {
181      const struct { enum StunAttributes value; const char *name; } attrs[] = {
182              { STUN_MAPPED_ADDRESS, "Mapped Address" },
183              { STUN_RESPONSE_ADDRESS, "Response Address" },
184              { STUN_CHANGE_ADDRESS, "Change Address" },
185              { STUN_SOURCE_ADDRESS, "Source Address" },
186              { STUN_CHANGED_ADDRESS, "Changed Address" },
187              { STUN_USERNAME, "Username" },
188              { STUN_PASSWORD, "Password" },
189              { STUN_MESSAGE_INTEGRITY, "Message Integrity" },
190              { STUN_ERROR_CODE, "Error Code" },
191              { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" },
192              { STUN_REFLECTED_FROM, "Reflected From" },
193              { STUN_REALM, "Realm" },
194              { STUN_NONCE, "Nonce" },
195              { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" },
196              { STUN_MS_VERSION, "MS Version" },
197              { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" },
198              { STUN_SOFTWARE, "Software" },
199              { STUN_ALTERNATE_SERVER, "Alternate Server" },
200              { STUN_FINGERPRINT, "Fingerprint" },
201              { 0, NULL }
202      };
203      int i;
204
205      for (i = 0; attrs[i].name; i++) {
206           if (attrs[i].value == msg)
207                return attrs[i].name;
208      }
209      return "Unknown Attribute";
210 }
211
212
213 /**
214  * Fill the stun_header with a random request_id
215  *
216  * @param state, STUN attribute type
217  * @param attr , the actual attribute
218  *
219  * @param req, stun header to be filled
220  */
221 static int stun_process_attr(struct StunState *state, struct stun_attr *attr)
222 {
223      LOG (GNUNET_ERROR_TYPE_INFO,
224           "Found STUN Attribute %s (%04x), length %d\n",
225           stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
226
227      switch (ntohs(attr->attr)) {
228           case STUN_MAPPED_ADDRESS:
229           case STUN_XOR_MAPPED_ADDRESS:
230           case STUN_MS_XOR_MAPPED_ADDRESS:
231                break;
232           default:
233                LOG (GNUNET_ERROR_TYPE_INFO,
234                     "Ignoring STUN Attribute %s (%04x), length %d\n",
235                     stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
236
237      }
238      return 0;
239 }
240
241
242
243 /**
244  * Fill the stun_header with a random request_id
245  *
246  * @param req, stun header to be filled
247  */
248 static void
249 generate_request_id(struct stun_header *req)
250 {
251      int x;
252      req->magic = htonl(STUN_MAGIC_COOKIE);
253      for (x = 0; x < 3; x++)
254           req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
255                                                     UINT32_MAX);
256 }
257
258
259 /**
260  * Extract the STUN_MAPPED_ADDRESS from the stun response.
261  * This is used as a callback for stun_handle_response
262  * when called from stun_request.
263  *
264  * @param st, pointer where we will set the type
265  * @param attr , received stun attribute
266  * @param arg , pointer to a sockaddr_in where we will set the reported IP and port
267  * @param magic , Magic cookie
268  *
269  * @return 0 on sucess, other value otherwise
270  */
271 static int
272 stun_get_mapped(struct StunState *st, struct stun_attr *attr,struct sockaddr_in *arg, unsigned int magic)
273 {
274     struct stun_addr *returned_addr = (struct stun_addr *)(attr + 1);
275     struct sockaddr_in *sa = (struct sockaddr_in *)arg;
276     unsigned short type = ntohs(attr->attr);
277
278     switch (type) {
279         case STUN_MAPPED_ADDRESS:
280             if (st->attr == STUN_XOR_MAPPED_ADDRESS ||
281                 st->attr == STUN_MS_XOR_MAPPED_ADDRESS)
282                 return 1;
283             magic = 0;
284             break;
285         case STUN_MS_XOR_MAPPED_ADDRESS:
286             if (st->attr == STUN_XOR_MAPPED_ADDRESS)
287                 return 1;
288             break;
289         case STUN_XOR_MAPPED_ADDRESS:
290             break;
291         default:
292             return 1;
293     }
294     if (ntohs(attr->len) < 8 && returned_addr->family != 1) {
295         return 1;
296     }
297
298     st->attr = type;
299     sa->sin_port = returned_addr->port ^ htons(ntohl(magic) >> 16);
300     sa->sin_addr.s_addr = returned_addr->addr ^ magic;
301     return 0;
302 }
303
304
305 /**
306  * Handle an incoming STUN message, Do some basic sanity checks on packet size and content,
307  * try to extract a bit of information, and possibly reply.
308  * At the moment this only processes BIND requests, and returns
309  * the externally visible address of the request.
310  * If a callback is specified, invoke it with the attribute.
311  *
312  * @param data, the packet
313  * @param len, the length of the packet
314  * @param arg, sockaddr_in where we will set our discovered packet
315  *
316  * @return, 0 on OK, -1 if the packet is invalid ( not a stun packet)
317  */
318 int
319 GNUNET_NAT_stun_handle_packet(const uint8_t *data, size_t len,struct sockaddr_in *arg)
320 {
321      struct stun_header *hdr = (struct stun_header *)data;
322      struct stun_attr *attr;
323      struct StunState st;
324      int ret = STUN_IGNORE;
325
326      uint32_t advertised_message_size;
327      uint32_t message_magic_cookie;
328
329
330      /* On entry, 'len' is the length of the udp payload. After the
331           * initial checks it becomes the size of unprocessed options,
332           * while 'data' is advanced accordingly.
333           */
334      if (len < sizeof(struct stun_header)) {
335           LOG (GNUNET_ERROR_TYPE_INFO,
336                "STUN packet too short (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
337           GNUNET_break_op (0);
338           return -1;
339      }
340      /* Skip header as it is already in hdr */
341      len -= sizeof(struct stun_header);
342      data += sizeof(struct stun_header);
343
344      /* len as advertised in the message */
345      advertised_message_size = ntohs(hdr->msglen);
346
347      message_magic_cookie = ntohl(hdr->magic);
348      /* Compare if the cookie match */
349      if(STUN_MAGIC_COOKIE != message_magic_cookie){
350           LOG (GNUNET_ERROR_TYPE_INFO,
351                "Invalid magic cookie \n");
352           GNUNET_break_op (0);
353           return -1;
354      }
355
356
357      LOG (GNUNET_ERROR_TYPE_INFO, "STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), advertised_message_size);
358
359
360      if (advertised_message_size > len) {
361           LOG (GNUNET_ERROR_TYPE_INFO, "Scrambled STUN packet length (got %d, expecting %d)\n", advertised_message_size, (int)len);
362           GNUNET_break_op (0);
363          return -1;
364      } else {
365           len = advertised_message_size;
366      }
367      /* Zero the struct */
368      memset(&st,0, sizeof(st));
369
370      while (len > 0) {
371           if (len < sizeof(struct stun_attr)) {
372                LOG (GNUNET_ERROR_TYPE_INFO, "Attribute too short (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
373                GNUNET_break_op (0);
374                break;
375           }
376           attr = (struct stun_attr *)data;
377
378           /* compute total attribute length */
379           advertised_message_size = ntohs(attr->len) + sizeof(struct stun_attr);
380
381           /* Check if we still have space in our buffer */
382           if (advertised_message_size > len ) {
383                LOG (GNUNET_ERROR_TYPE_INFO, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", advertised_message_size, (int)len);
384                GNUNET_break_op (0);
385                break;
386           }
387
388
389          stun_get_mapped(&st, attr, arg, hdr->magic);
390
391           if (stun_process_attr(&st, attr)) {
392                LOG (GNUNET_ERROR_TYPE_INFO, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
393                break;
394           }
395           /* Clear attribute id: in case previous entry was a string,
396                    * this will act as the terminator for the string.
397                    */
398           attr->attr = 0;
399           data += advertised_message_size;
400           len -= advertised_message_size;
401      }
402
403      return ret;
404 }
405
406
407
408 /**
409  * Try to establish a connection given the specified address.
410  *
411  * @param cls our `struct GNUNET_NAT_StunRequestHandle *`
412  * @param addr address to try, NULL for "last call"
413  * @param addrlen length of @a addr
414  */
415 static void
416 stun_dns_callback (void *cls,
417                            const struct sockaddr *addr,
418                            socklen_t addrlen) {
419
420
421      struct GNUNET_NAT_StunRequestHandle *request = cls;
422
423      struct stun_header *req;
424      uint8_t reqdata[1024];
425      int reqlen;
426      struct sockaddr_in server;
427
428      if(NULL == request) {
429           LOG (GNUNET_ERROR_TYPE_INFO, "Empty request\n");
430           /* FIXME clean up ? */
431           return;
432      }
433
434      if (NULL == addr) {
435           request->dns_active = NULL;
436           LOG (GNUNET_ERROR_TYPE_INFO, "Error resolving host %s\n", request->stun_server);
437           /* FIXME clean up? */
438           return;
439      }
440
441
442      memset(&server,0, sizeof(server));
443      server.sin_family = AF_INET;
444      server.sin_addr = ((struct sockaddr_in *)addr)->sin_addr;
445      server.sin_port = htons(request->stun_port);
446
447
448      /*Craft the simplest possible STUN packet. A request binding*/
449      req = (struct stun_header *)reqdata;
450      generate_request_id(req);
451      reqlen = 0;
452      req->msgtype = 0;
453      req->msglen = 0;
454      req->msglen = htons(reqlen);
455      req->msgtype = htons(encode_message(STUN_REQUEST, STUN_BINDING));
456
457      /* Send the packet */
458      if (-1 == GNUNET_NETWORK_socket_sendto (request->sock, req, ntohs(req->msglen) + sizeof(*req),
459                                              (const struct sockaddr *) &server, sizeof (server)))
460      {
461           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "Fail to sendto");
462      }
463
464 }
465
466
467 /**
468  * Make Generic STUN request and
469  * Send a generic stun request to the server specified using the specified socket.
470  * possibly waiting for a reply and filling the 'reply' field with
471  * the externally visible address.
472  *c
473  * @param server, the address of the stun server
474  * @param port, port of the stun server
475  * @param sock the socket used to send the request
476  * @return GNUNET_NAT_StunRequestHandle on success, NULL on error.
477  */
478 struct GNUNET_NAT_StunRequestHandle *
479 GNUNET_NAT_stun_make_request(char * server, int port, struct GNUNET_NETWORK_Handle * sock)
480 {
481
482      struct GNUNET_NAT_StunRequestHandle *rh;
483
484      rh = GNUNET_malloc (sizeof (struct GNUNET_NAT_StunRequestHandle));
485      rh->sock = sock;
486
487      char * server_copy = GNUNET_malloc (1 + strlen (server));
488      if (server_copy) {
489         strcpy (server_copy, server);
490      }else{
491          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "Failed to allocate string");
492          /* FIXME: cleanup rh? */
493          return NULL;
494      }
495
496      rh->stun_server = server_copy;
497      rh->stun_port = port;
498      rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, AF_INET,
499                                               GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
500                                               &stun_dns_callback, rh);
501      /* FIXME: error handling NULL==dns_active, callback function? */
502
503      return rh;
504 }