diff options
Diffstat (limited to 'src/nat/nat_stun.c')
-rw-r--r-- | src/nat/nat_stun.c | 439 |
1 files changed, 0 insertions, 439 deletions
diff --git a/src/nat/nat_stun.c b/src/nat/nat_stun.c deleted file mode 100644 index 62916ab84..000000000 --- a/src/nat/nat_stun.c +++ /dev/null | |||
@@ -1,439 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2015 GNUnet e.V. | ||
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., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * This code provides some support for doing STUN transactions. | ||
22 | * We send simplest possible packet ia REQUEST with BIND to a STUN server. | ||
23 | * | ||
24 | * All STUN packets start with a simple header made of a type, | ||
25 | * length (excluding the header) and a 16-byte random transaction id. | ||
26 | * Following the header we may have zero or more attributes, each | ||
27 | * structured as a type, length and a value (whose format depends | ||
28 | * on the type, but often contains addresses). | ||
29 | * Of course all fields are in network format. | ||
30 | * | ||
31 | * This code was based on ministun.c. | ||
32 | * | ||
33 | * @file nat/nat_stun.c | ||
34 | * @brief Functions for STUN functionality | ||
35 | * @author Bruno Souza Cabral | ||
36 | */ | ||
37 | |||
38 | #include "platform.h" | ||
39 | #include "gnunet_util_lib.h" | ||
40 | #include "gnunet_resolver_service.h" | ||
41 | #include "gnunet_nat_lib.h" | ||
42 | |||
43 | |||
44 | #include "nat_stun.h" | ||
45 | |||
46 | #define LOG(kind,...) GNUNET_log_from (kind, "stun", __VA_ARGS__) | ||
47 | |||
48 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Handle to a request given to the resolver. Can be used to cancel | ||
53 | * the request prior to the timeout or successful execution. Also | ||
54 | * used to track our internal state for the request. | ||
55 | */ | ||
56 | struct GNUNET_NAT_STUN_Handle | ||
57 | { | ||
58 | |||
59 | /** | ||
60 | * Handle to a pending DNS lookup request. | ||
61 | */ | ||
62 | struct GNUNET_RESOLVER_RequestHandle *dns_active; | ||
63 | |||
64 | /** | ||
65 | * Handle to the listen socket | ||
66 | */ | ||
67 | struct GNUNET_NETWORK_Handle *sock; | ||
68 | |||
69 | /** | ||
70 | * Stun server address | ||
71 | */ | ||
72 | char *stun_server; | ||
73 | |||
74 | /** | ||
75 | * Function to call when a error occours | ||
76 | */ | ||
77 | GNUNET_NAT_STUN_ErrorCallback cb; | ||
78 | |||
79 | /** | ||
80 | * Closure for @e cb. | ||
81 | */ | ||
82 | void *cb_cls; | ||
83 | |||
84 | /** | ||
85 | * Do we got a DNS resolution successfully? | ||
86 | */ | ||
87 | int dns_success; | ||
88 | |||
89 | /** | ||
90 | * STUN port | ||
91 | */ | ||
92 | uint16_t stun_port; | ||
93 | |||
94 | }; | ||
95 | |||
96 | |||
97 | /** | ||
98 | * here we store credentials extracted from a message | ||
99 | */ | ||
100 | struct StunState | ||
101 | { | ||
102 | uint16_t attr; | ||
103 | }; | ||
104 | |||
105 | |||
106 | /** | ||
107 | * Encode a class and method to a compatible STUN format | ||
108 | * | ||
109 | * @param msg_class class to be converted | ||
110 | * @param method method to be converted | ||
111 | * @return message in a STUN compatible format | ||
112 | */ | ||
113 | static int | ||
114 | encode_message (enum StunClasses msg_class, | ||
115 | enum StunMethods method) | ||
116 | { | ||
117 | return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) | | ||
118 | (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) << 2); | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Fill the stun_header with a random request_id | ||
124 | * | ||
125 | * @param req, stun header to be filled | ||
126 | */ | ||
127 | static void | ||
128 | generate_request_id (struct stun_header *req) | ||
129 | { | ||
130 | unsigned int x; | ||
131 | |||
132 | req->magic = htonl(STUN_MAGIC_COOKIE); | ||
133 | for (x = 0; x < 3; x++) | ||
134 | req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
135 | UINT32_MAX); | ||
136 | } | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Extract the STUN_MAPPED_ADDRESS from the stun response. | ||
141 | * This is used as a callback for stun_handle_response | ||
142 | * when called from stun_request. | ||
143 | * | ||
144 | * @param st, pointer where we will set the type | ||
145 | * @param attr , received stun attribute | ||
146 | * @param arg , pointer to a sockaddr_in where we will set the reported IP and port | ||
147 | * @param magic , Magic cookie | ||
148 | * | ||
149 | * @return 0 on success, other value otherwise | ||
150 | */ | ||
151 | static int | ||
152 | stun_get_mapped (struct StunState *st, | ||
153 | struct stun_attr *attr, | ||
154 | struct sockaddr_in *arg, | ||
155 | unsigned int magic) | ||
156 | { | ||
157 | struct stun_addr *returned_addr = (struct stun_addr *)(attr + 1); | ||
158 | struct sockaddr_in *sa = (struct sockaddr_in *)arg; | ||
159 | unsigned short type = ntohs(attr->attr); | ||
160 | |||
161 | switch (type) | ||
162 | { | ||
163 | case STUN_MAPPED_ADDRESS: | ||
164 | if (st->attr == STUN_XOR_MAPPED_ADDRESS || | ||
165 | st->attr == STUN_MS_XOR_MAPPED_ADDRESS) | ||
166 | return 1; | ||
167 | magic = 0; | ||
168 | break; | ||
169 | case STUN_MS_XOR_MAPPED_ADDRESS: | ||
170 | if (st->attr == STUN_XOR_MAPPED_ADDRESS) | ||
171 | return 1; | ||
172 | break; | ||
173 | case STUN_XOR_MAPPED_ADDRESS: | ||
174 | break; | ||
175 | default: | ||
176 | return 1; | ||
177 | } | ||
178 | if ( (ntohs(attr->len) < 8) && | ||
179 | (returned_addr->family != 1) ) | ||
180 | { | ||
181 | return 1; | ||
182 | } | ||
183 | st->attr = type; | ||
184 | sa->sin_family = AF_INET; | ||
185 | sa->sin_port = returned_addr->port ^ htons(ntohl(magic) >> 16); | ||
186 | sa->sin_addr.s_addr = returned_addr->addr ^ magic; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | |||
191 | /** | ||
192 | * Handle an incoming STUN message, Do some basic sanity checks on packet size and content, | ||
193 | * try to extract a bit of information, and possibly reply. | ||
194 | * At the moment this only processes BIND requests, and returns | ||
195 | * the externally visible address of the request. | ||
196 | * If a callback is specified, invoke it with the attribute. | ||
197 | * | ||
198 | * @param data the packet | ||
199 | * @param len the length of the packet in @a data | ||
200 | * @param[out] arg sockaddr_in where we will set our discovered address | ||
201 | * | ||
202 | * @return, #GNUNET_OK on OK, #GNUNET_NO if the packet is invalid (not a stun packet) | ||
203 | */ | ||
204 | int | ||
205 | GNUNET_NAT_stun_handle_packet (const void *data, | ||
206 | size_t len, | ||
207 | struct sockaddr_in *arg) | ||
208 | { | ||
209 | const struct stun_header *hdr = (const struct stun_header *)data; | ||
210 | struct stun_attr *attr; | ||
211 | struct StunState st; | ||
212 | int ret = GNUNET_OK; | ||
213 | uint32_t advertised_message_size; | ||
214 | uint32_t message_magic_cookie; | ||
215 | |||
216 | /* On entry, 'len' is the length of the udp payload. After the | ||
217 | * initial checks it becomes the size of unprocessed options, | ||
218 | * while 'data' is advanced accordingly. | ||
219 | */ | ||
220 | if (len < sizeof(struct stun_header)) | ||
221 | { | ||
222 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
223 | "STUN packet too short (only %d, wanting at least %d)\n", | ||
224 | (int) len, | ||
225 | (int) sizeof(struct stun_header)); | ||
226 | GNUNET_break_op (0); | ||
227 | return GNUNET_NO; | ||
228 | } | ||
229 | /* Skip header as it is already in hdr */ | ||
230 | len -= sizeof(struct stun_header); | ||
231 | data += sizeof(struct stun_header); | ||
232 | |||
233 | /* len as advertised in the message */ | ||
234 | advertised_message_size = ntohs(hdr->msglen); | ||
235 | |||
236 | message_magic_cookie = ntohl(hdr->magic); | ||
237 | /* Compare if the cookie match */ | ||
238 | if (STUN_MAGIC_COOKIE != message_magic_cookie) | ||
239 | { | ||
240 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
241 | "Invalid magic cookie \n"); | ||
242 | return GNUNET_NO; | ||
243 | } | ||
244 | |||
245 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
246 | "STUN Packet, msg %s (%04x), length: %d\n", | ||
247 | stun_msg2str(ntohs(hdr->msgtype)), | ||
248 | ntohs(hdr->msgtype), | ||
249 | advertised_message_size); | ||
250 | if (advertised_message_size > len) | ||
251 | { | ||
252 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
253 | "Scrambled STUN packet length (got %d, expecting %d)\n", | ||
254 | advertised_message_size, | ||
255 | (int)len); | ||
256 | return GNUNET_NO; | ||
257 | } | ||
258 | len = advertised_message_size; | ||
259 | memset (&st, 0, sizeof(st)); | ||
260 | |||
261 | while (len > 0) | ||
262 | { | ||
263 | if (len < sizeof(struct stun_attr)) | ||
264 | { | ||
265 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
266 | "Attribute too short (got %d, expecting %d)\n", | ||
267 | (int)len, | ||
268 | (int) sizeof(struct stun_attr)); | ||
269 | break; | ||
270 | } | ||
271 | attr = (struct stun_attr *)data; | ||
272 | |||
273 | /* compute total attribute length */ | ||
274 | advertised_message_size = ntohs(attr->len) + sizeof(struct stun_attr); | ||
275 | |||
276 | /* Check if we still have space in our buffer */ | ||
277 | if (advertised_message_size > len ) | ||
278 | { | ||
279 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
280 | "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", | ||
281 | advertised_message_size, | ||
282 | (int)len); | ||
283 | break; | ||
284 | } | ||
285 | stun_get_mapped (&st, | ||
286 | attr, | ||
287 | arg, | ||
288 | hdr->magic); | ||
289 | /* Clear attribute id: in case previous entry was a string, | ||
290 | * this will act as the terminator for the string. | ||
291 | */ | ||
292 | attr->attr = 0; | ||
293 | data += advertised_message_size; | ||
294 | len -= advertised_message_size; | ||
295 | ret = GNUNET_OK; | ||
296 | } | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | |||
301 | /** | ||
302 | * Cancel active STUN request. Frees associated resources | ||
303 | * and ensures that the callback is no longer invoked. | ||
304 | * | ||
305 | * @param rh request to cancel | ||
306 | */ | ||
307 | void | ||
308 | GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh) | ||
309 | { | ||
310 | if (NULL != rh->dns_active) | ||
311 | { | ||
312 | GNUNET_RESOLVER_request_cancel (rh->dns_active); | ||
313 | rh->dns_active = NULL; | ||
314 | } | ||
315 | GNUNET_free (rh->stun_server); | ||
316 | GNUNET_free (rh); | ||
317 | } | ||
318 | |||
319 | |||
320 | /** | ||
321 | * Try to establish a connection given the specified address. | ||
322 | * | ||
323 | * @param cls our `struct GNUNET_NAT_STUN_Handle *` | ||
324 | * @param addr address to try, NULL for "last call" | ||
325 | * @param addrlen length of @a addr | ||
326 | */ | ||
327 | static void | ||
328 | stun_dns_callback (void *cls, | ||
329 | const struct sockaddr *addr, | ||
330 | socklen_t addrlen) | ||
331 | { | ||
332 | struct GNUNET_NAT_STUN_Handle *rh = cls; | ||
333 | struct stun_header *req; | ||
334 | uint8_t reqdata[1024]; | ||
335 | int reqlen; | ||
336 | struct sockaddr_in server; | ||
337 | |||
338 | if (NULL == addr) | ||
339 | { | ||
340 | rh->dns_active = NULL; | ||
341 | if (GNUNET_NO == rh->dns_success) | ||
342 | { | ||
343 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
344 | "Error resolving host %s\n", | ||
345 | rh->stun_server); | ||
346 | rh->cb (rh->cb_cls, | ||
347 | GNUNET_NAT_ERROR_NOT_ONLINE); | ||
348 | } | ||
349 | else if (GNUNET_SYSERR == rh->dns_success) | ||
350 | { | ||
351 | rh->cb (rh->cb_cls, | ||
352 | GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | rh->cb (rh->cb_cls, | ||
357 | GNUNET_NAT_ERROR_SUCCESS); | ||
358 | } | ||
359 | GNUNET_NAT_stun_make_request_cancel (rh); | ||
360 | return; | ||
361 | } | ||
362 | |||
363 | rh->dns_success = GNUNET_YES; | ||
364 | memset (&server,0, sizeof(server)); | ||
365 | server.sin_family = AF_INET; | ||
366 | server.sin_addr = ((struct sockaddr_in *)addr)->sin_addr; | ||
367 | server.sin_port = htons(rh->stun_port); | ||
368 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
369 | server.sin_len = (u_char) sizeof (struct sockaddr_in); | ||
370 | #endif | ||
371 | |||
372 | /*Craft the simplest possible STUN packet. A request binding*/ | ||
373 | req = (struct stun_header *)reqdata; | ||
374 | generate_request_id (req); | ||
375 | reqlen = 0; | ||
376 | req->msgtype = 0; | ||
377 | req->msglen = 0; | ||
378 | req->msglen = htons (reqlen); | ||
379 | req->msgtype = htons (encode_message (STUN_REQUEST, | ||
380 | STUN_BINDING)); | ||
381 | |||
382 | /* Send the packet */ | ||
383 | if (-1 == | ||
384 | GNUNET_NETWORK_socket_sendto (rh->sock, | ||
385 | req, | ||
386 | ntohs(req->msglen) + sizeof(*req), | ||
387 | (const struct sockaddr *) &server, | ||
388 | sizeof (server))) | ||
389 | { | ||
390 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, | ||
391 | "sendto"); | ||
392 | rh->dns_success = GNUNET_SYSERR; | ||
393 | return; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | |||
398 | /** | ||
399 | * Make Generic STUN request. Sends a generic stun request to the | ||
400 | * server specified using the specified socket, possibly waiting for | ||
401 | * a reply and filling the 'reply' field with the externally visible | ||
402 | * address. | ||
403 | * | ||
404 | * @param server the address of the stun server | ||
405 | * @param port port of the stun server | ||
406 | * @param sock the socket used to send the request | ||
407 | * @param cb callback in case of error | ||
408 | * @param cb_cls closure for @a cb | ||
409 | * @return NULL on error | ||
410 | */ | ||
411 | struct GNUNET_NAT_STUN_Handle * | ||
412 | GNUNET_NAT_stun_make_request (const char *server, | ||
413 | uint16_t port, | ||
414 | struct GNUNET_NETWORK_Handle *sock, | ||
415 | GNUNET_NAT_STUN_ErrorCallback cb, | ||
416 | void *cb_cls) | ||
417 | { | ||
418 | struct GNUNET_NAT_STUN_Handle *rh; | ||
419 | |||
420 | rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle); | ||
421 | rh->sock = sock; | ||
422 | rh->cb = cb; | ||
423 | rh->cb_cls = cb_cls; | ||
424 | rh->stun_server = GNUNET_strdup (server); | ||
425 | rh->stun_port = port; | ||
426 | rh->dns_success = GNUNET_NO; | ||
427 | rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, | ||
428 | AF_INET, | ||
429 | TIMEOUT, | ||
430 | &stun_dns_callback, rh); | ||
431 | if (NULL == rh->dns_active) | ||
432 | { | ||
433 | GNUNET_NAT_stun_make_request_cancel (rh); | ||
434 | return NULL; | ||
435 | } | ||
436 | return rh; | ||
437 | } | ||
438 | |||
439 | /* end of nat_stun.c */ | ||