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