aboutsummaryrefslogtreecommitdiff
path: root/src/nat/nat_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nat/nat_api.c')
-rw-r--r--src/nat/nat_api.c718
1 files changed, 0 insertions, 718 deletions
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c
deleted file mode 100644
index 20e7b6ec6..000000000
--- a/src/nat/nat_api.c
+++ /dev/null
@@ -1,718 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007-2017 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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 * @author Milan Bouchet-Valat
24 *
25 * @file nat/nat_api.c
26 * Service for handling UPnP and NAT-PMP port forwarding
27 * and external IP address retrieval
28 */
29#include "platform.h"
30#include "gnunet_nat_service.h"
31#include "nat.h"
32#include "nat_stun.h"
33
34
35/**
36 * Entry in DLL of addresses of this peer.
37 */
38struct AddrEntry
39{
40 /**
41 * DLL.
42 */
43 struct AddrEntry *next;
44
45 /**
46 * DLL.
47 */
48 struct AddrEntry *prev;
49
50 /**
51 * Place where the application can store data (on add,
52 * and retrieve on remove).
53 */
54 void *app_ctx;
55
56 /**
57 * Address class of the address.
58 */
59 enum GNUNET_NAT_AddressClass ac;
60
61 /**
62 * Number of bytes that follow.
63 */
64 socklen_t addrlen;
65};
66
67
68/**
69 * Handle for active NAT registrations.
70 */
71struct GNUNET_NAT_Handle
72{
73 /**
74 * Configuration we use.
75 */
76 const struct GNUNET_CONFIGURATION_Handle *cfg;
77
78 /**
79 * Message queue for communicating with the NAT service.
80 */
81 struct GNUNET_MQ_Handle *mq;
82
83 /**
84 * Our registration message.
85 */
86 struct GNUNET_MessageHeader *reg;
87
88 /**
89 * Head of address DLL.
90 */
91 struct AddrEntry *ae_head;
92
93 /**
94 * Tail of address DLL.
95 */
96 struct AddrEntry *ae_tail;
97
98 /**
99 * Function to call when our addresses change.
100 */
101 GNUNET_NAT_AddressCallback address_callback;
102
103 /**
104 * Function to call when another peer requests connection reversal.
105 */
106 GNUNET_NAT_ReversalCallback reversal_callback;
107
108 /**
109 * Closure for the various callbacks.
110 */
111 void *callback_cls;
112
113 /**
114 * Task scheduled to reconnect to the service.
115 */
116 struct GNUNET_SCHEDULER_Task *reconnect_task;
117
118 /**
119 * How long to wait until we reconnect.
120 */
121 struct GNUNET_TIME_Relative reconnect_delay;
122};
123
124
125/**
126 * Task to connect to the NAT service.
127 *
128 * @param cls our `struct GNUNET_NAT_Handle *`
129 */
130static void
131do_connect (void *cls);
132
133
134/**
135 * Task to connect to the NAT service.
136 *
137 * @param nh handle to reconnect
138 */
139static void
140reconnect (struct GNUNET_NAT_Handle *nh)
141{
142 struct AddrEntry *ae;
143
144 if (NULL != nh->mq)
145 {
146 GNUNET_MQ_destroy (nh->mq);
147 nh->mq = NULL;
148 }
149 while (NULL != (ae = nh->ae_head))
150 {
151 GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
152 nh->address_callback (nh->callback_cls,
153 &ae->app_ctx,
154 GNUNET_NO,
155 ae->ac,
156 (const struct sockaddr *) &ae[1],
157 ae->addrlen);
158 GNUNET_free (ae);
159 }
160 nh->reconnect_delay = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay);
161 nh->reconnect_task =
162 GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay, &do_connect, nh);
163}
164
165
166/**
167 * Check connection reversal request.
168 *
169 * @param cls our `struct GNUNET_NAT_Handle`
170 * @param crm the message
171 * @return #GNUNET_OK if @a crm is well-formed
172 */
173static int
174check_connection_reversal_request (
175 void *cls,
176 const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
177{
178 if (ntohs (crm->header.size) != sizeof(*crm) + sizeof(struct sockaddr_in))
179 {
180 GNUNET_break (0);
181 return GNUNET_SYSERR;
182 }
183 return GNUNET_OK;
184}
185
186
187/**
188 * Handle connection reversal request.
189 *
190 * @param cls our `struct GNUNET_NAT_Handle`
191 * @param crm the message
192 */
193static void
194handle_connection_reversal_request (
195 void *cls,
196 const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
197{
198 struct GNUNET_NAT_Handle *nh = cls;
199
200 nh->reversal_callback (nh->callback_cls,
201 (const struct sockaddr *) &crm[1],
202 sizeof(struct sockaddr_in));
203}
204
205
206/**
207 * Check address change notification.
208 *
209 * @param cls our `struct GNUNET_NAT_Handle`
210 * @param acn the message
211 * @return #GNUNET_OK if @a crm is well-formed
212 */
213static int
214check_address_change_notification (
215 void *cls,
216 const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
217{
218 size_t alen = ntohs (acn->header.size) - sizeof(*acn);
219
220 switch (alen)
221 {
222 case sizeof(struct sockaddr_in): {
223 const struct sockaddr_in *s4 = (const struct sockaddr_in *) &acn[1];
224 if (AF_INET != s4->sin_family)
225 {
226 GNUNET_break (0);
227 return GNUNET_SYSERR;
228 }
229 }
230 break;
231
232 case sizeof(struct sockaddr_in6): {
233 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) &acn[1];
234 if (AF_INET6 != s6->sin6_family)
235 {
236 GNUNET_break (0);
237 return GNUNET_SYSERR;
238 }
239 }
240 break;
241
242 default:
243 GNUNET_break (0);
244 return GNUNET_SYSERR;
245 }
246 return GNUNET_OK;
247}
248
249
250/**
251 * Handle connection reversal request.
252 *
253 * @param cls our `struct GNUNET_NAT_Handle`
254 * @param acn the message
255 */
256static void
257handle_address_change_notification (
258 void *cls,
259 const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
260{
261 struct GNUNET_NAT_Handle *nh = cls;
262 size_t alen = ntohs (acn->header.size) - sizeof(*acn);
263 const struct sockaddr *sa = (const struct sockaddr *) &acn[1];
264 enum GNUNET_NAT_AddressClass ac;
265 struct AddrEntry *ae;
266
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Received address change notification\n");
269 ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class);
270 if (GNUNET_YES == ntohl (acn->add_remove))
271 {
272 ae = GNUNET_malloc (sizeof(*ae) + alen);
273 ae->ac = ac;
274 ae->addrlen = alen;
275 GNUNET_memcpy (&ae[1], sa, alen);
276 GNUNET_CONTAINER_DLL_insert (nh->ae_head, nh->ae_tail, ae);
277 nh->address_callback (nh->callback_cls,
278 &ae->app_ctx,
279 ntohl (acn->add_remove),
280 ac,
281 sa,
282 alen);
283 }
284 else
285 {
286 for (ae = nh->ae_head; NULL != ae; ae = ae->next)
287 if ((ae->addrlen == alen) && (0 == memcmp (&ae[1], sa, alen)))
288 break;
289 if (NULL == ae)
290 {
291 GNUNET_break (0);
292 reconnect (nh);
293 return;
294 }
295 GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
296 nh->address_callback (nh->callback_cls,
297 &ae->app_ctx,
298 ntohl (acn->add_remove),
299 ac,
300 sa,
301 alen);
302 GNUNET_free (ae);
303 }
304}
305
306
307/**
308 * Handle queue errors by reconnecting to NAT.
309 *
310 * @param cls the `struct GNUNET_NAT_Handle *`
311 * @param error details about the error
312 */
313static void
314mq_error_handler (void *cls,
315 enum GNUNET_MQ_Error error)
316{
317 struct GNUNET_NAT_Handle *nh = cls;
318
319 reconnect (nh);
320}
321
322
323/**
324 * Task to connect to the NAT service.
325 *
326 * @param cls our `struct GNUNET_NAT_Handle *`
327 */
328static void
329do_connect (void *cls)
330{
331 struct GNUNET_NAT_Handle *nh = cls;
332 struct GNUNET_MQ_MessageHandler handlers[] = {
333 GNUNET_MQ_hd_var_size (
334 connection_reversal_request,
335 GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED,
336 struct GNUNET_NAT_ConnectionReversalRequestedMessage,
337 nh),
338 GNUNET_MQ_hd_var_size (
339 address_change_notification,
340 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE,
341 struct GNUNET_NAT_AddressChangeNotificationMessage,
342 nh),
343 GNUNET_MQ_handler_end ()
344 };
345 struct GNUNET_MQ_Envelope *env;
346
347 nh->reconnect_task = NULL;
348 nh->mq =
349 GNUNET_CLIENT_connect (nh->cfg,
350 "nat",
351 handlers,
352 &mq_error_handler,
353 nh);
354 if (NULL == nh->mq)
355 {
356 reconnect (nh);
357 return;
358 }
359 env = GNUNET_MQ_msg_copy (nh->reg);
360 GNUNET_MQ_send (nh->mq,
361 env);
362}
363
364
365/**
366 * Attempt to enable port redirection and detect public IP address
367 * contacting UPnP or NAT-PMP routers on the local network. Use @a
368 * addr to specify to which of the local host's addresses should the
369 * external port be mapped. The port is taken from the corresponding
370 * sockaddr_in[6] field. The NAT module should call the given @a
371 * address_callback for any 'plausible' external address.
372 *
373 * @param cfg configuration to use
374 * @param config_section name of the configuration section for optionsx
375 * @param proto protocol this is about, IPPROTO_TCP or IPPROTO_UDP
376 * @param num_addrs number of addresses in @a addrs
377 * @param addrs list of local addresses packets should be redirected to
378 * @param addrlens actual lengths of the addresses in @a addrs
379 * @param address_callback function to call every time the public IP address changes
380 * @param reversal_callback function to call if someone wants connection reversal from us,
381 * NULL if connection reversal is not supported
382 * @param callback_cls closure for callbacks
383 * @return NULL on error, otherwise handle that can be used to unregister
384 */
385struct GNUNET_NAT_Handle *
386GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg,
387 const char *config_section,
388 uint8_t proto,
389 unsigned int num_addrs,
390 const struct sockaddr **addrs,
391 const socklen_t *addrlens,
392 GNUNET_NAT_AddressCallback address_callback,
393 GNUNET_NAT_ReversalCallback reversal_callback,
394 void *callback_cls)
395{
396 struct GNUNET_NAT_Handle *nh;
397 struct GNUNET_NAT_RegisterMessage *rm;
398 size_t len;
399 size_t str_len;
400 char *off;
401
402 len = 0;
403 for (unsigned int i = 0; i < num_addrs; i++)
404 len += addrlens[i];
405 str_len = strlen (config_section) + 1;
406 len += str_len;
407 if ( (len > GNUNET_MAX_MESSAGE_SIZE - sizeof(*rm)) ||
408 (num_addrs > UINT16_MAX) ||
409 (str_len > UINT16_MAX) )
410 {
411 GNUNET_break (0);
412 return NULL;
413 }
414 rm = GNUNET_malloc (sizeof(*rm) + len);
415 rm->header.size = htons (sizeof(*rm) + len);
416 rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER);
417 rm->flags = GNUNET_NAT_RF_NONE;
418 if (NULL != address_callback)
419 rm->flags |= GNUNET_NAT_RF_ADDRESSES;
420 if (NULL != reversal_callback)
421 rm->flags |= GNUNET_NAT_RF_REVERSAL;
422 rm->proto = proto;
423 rm->str_len = htons (str_len);
424 rm->num_addrs = htons ((uint16_t) num_addrs);
425 off = (char *) &rm[1];
426 for (unsigned int i = 0; i < num_addrs; i++)
427 {
428 switch (addrs[i]->sa_family)
429 {
430 case AF_INET:
431 if (sizeof(struct sockaddr_in) != addrlens[i])
432 {
433 GNUNET_break (0);
434 GNUNET_free (rm);
435 return NULL;
436 }
437 break;
438
439 case AF_INET6:
440 if (sizeof(struct sockaddr_in6) != addrlens[i])
441 {
442 GNUNET_break (0);
443 GNUNET_free (rm);
444 return NULL;
445 }
446 break;
447
448#if AF_UNIX
449 case AF_UNIX:
450 if (sizeof(struct sockaddr_un) != addrlens[i])
451 {
452 GNUNET_break (0);
453 GNUNET_free (rm);
454 return NULL;
455 }
456 break;
457#endif
458 default:
459 GNUNET_break (0);
460 GNUNET_free (rm);
461 return NULL;
462 }
463 GNUNET_memcpy (off, addrs[i], addrlens[i]);
464 off += addrlens[i];
465 }
466 GNUNET_memcpy (off, config_section, str_len);
467
468 nh = GNUNET_new (struct GNUNET_NAT_Handle);
469 nh->reg = &rm->header;
470 nh->cfg = cfg;
471 nh->address_callback = address_callback;
472 nh->reversal_callback = reversal_callback;
473 nh->callback_cls = callback_cls;
474 do_connect (nh);
475 return nh;
476}
477
478
479/**
480 * Check if an incoming message is a STUN message.
481 *
482 * @param data the packet
483 * @param len the length of the packet in @a data
484 * @return #GNUNET_YES if @a data is a STUN packet,
485 * #GNUNET_NO if the packet is invalid (not a stun packet)
486 */
487static enum GNUNET_GenericReturnValue
488test_stun_packet (const void *data, size_t len)
489{
490 const struct stun_header *hdr;
491 const struct stun_attr *attr;
492 uint32_t advertised_message_size;
493 uint32_t message_magic_cookie;
494
495 /* On entry, 'len' is the length of the UDP payload. After the
496 * initial checks it becomes the size of unprocessed options,
497 * while 'data' is advanced accordingly.
498 */
499 if (len < sizeof(struct stun_header))
500 {
501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 "STUN packet too short (only %d, wanting at least %d)\n",
503 (int) len,
504 (int) sizeof(struct stun_header));
505 return GNUNET_NO;
506 }
507 hdr = (const struct stun_header *) data;
508 /* Skip header as it is already in hdr */
509 len -= sizeof(struct stun_header);
510 data += sizeof(struct stun_header);
511
512 /* len as advertised in the message */
513 advertised_message_size = ntohs (hdr->msglen);
514
515 message_magic_cookie = ntohl (hdr->magic);
516 /* Compare if the cookie match */
517 if (STUN_MAGIC_COOKIE != message_magic_cookie)
518 {
519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid magic cookie for STUN\n");
520 return GNUNET_NO;
521 }
522
523 if (advertised_message_size > len)
524 {
525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526 "Scrambled STUN packet length (got %d, expecting %d)\n",
527 advertised_message_size,
528 (int) len);
529 return GNUNET_NO;
530 }
531 len = advertised_message_size;
532 while (len > 0)
533 {
534 if (len < sizeof(struct stun_attr))
535 {
536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537 "Attribute too short in STUN packet (got %d, expecting %d)\n",
538 (int) len,
539 (int) sizeof(struct stun_attr));
540 return GNUNET_NO;
541 }
542 attr = (const struct stun_attr *) data;
543
544 /* compute total attribute length */
545 advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
546
547 /* Check if we still have space in our buffer */
548 if (advertised_message_size > len)
549 {
550 GNUNET_log (
551 GNUNET_ERROR_TYPE_DEBUG,
552 "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n",
553 advertised_message_size,
554 (int) len);
555 return GNUNET_NO;
556 }
557 data += advertised_message_size;
558 len -= advertised_message_size;
559 }
560 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561 "STUN Packet, msg %04x, length: %d\n",
562 ntohs (hdr->msgtype),
563 advertised_message_size);
564 return GNUNET_OK;
565}
566
567
568/**
569 * Handle an incoming STUN message. This function is useful as
570 * some GNUnet service may be listening on a UDP port and might
571 * thus receive STUN messages while trying to receive other data.
572 * In this case, this function can be used to process replies
573 * to STUN requests.
574 *
575 * The function does some basic sanity checks on packet size and
576 * content, try to extract a bit of information.
577 *
578 * At the moment this only processes BIND requests, and returns the
579 * externally visible address of the request to the rest of the
580 * NAT logic.
581 *
582 * @param nh handle to the NAT service
583 * @param sender_addr address from which we got @a data
584 * @param sender_addr_len number of bytes in @a sender_addr
585 * @param data the packet
586 * @param data_size number of bytes in @a data
587 * @return #GNUNET_OK on success
588 * #GNUNET_NO if the packet is not a STUN packet
589 * #GNUNET_SYSERR on internal error handling the packet
590 */
591int
592GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh,
593 const struct sockaddr *sender_addr,
594 size_t sender_addr_len,
595 const void *data,
596 size_t data_size)
597{
598 struct GNUNET_MQ_Envelope *env;
599 struct GNUNET_NAT_HandleStunMessage *hsn;
600 char *buf;
601
602 if (GNUNET_YES != test_stun_packet (data, data_size))
603 return GNUNET_NO;
604 if (NULL == nh->mq)
605 return GNUNET_SYSERR;
606 env = GNUNET_MQ_msg_extra (hsn,
607 data_size + sender_addr_len,
608 GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN);
609 hsn->sender_addr_size = htons ((uint16_t) sender_addr_len);
610 hsn->payload_size = htons ((uint16_t) data_size);
611 buf = (char *) &hsn[1];
612 GNUNET_memcpy (buf, sender_addr, sender_addr_len);
613 buf += sender_addr_len;
614 GNUNET_memcpy (buf, data, data_size);
615 GNUNET_MQ_send (nh->mq, env);
616 return GNUNET_OK;
617}
618
619
620/**
621 * Test if the given address is (currently) a plausible IP address for
622 * this peer. Mostly a convenience function so that clients do not
623 * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback
624 * has returned so far.
625 *
626 * @param nh the handle returned by register
627 * @param addr IP address to test (IPv4 or IPv6)
628 * @param addrlen number of bytes in @a addr
629 * @return #GNUNET_YES if the address is plausible,
630 * #GNUNET_NO if the address is not plausible,
631 * #GNUNET_SYSERR if the address is malformed
632 */
633int
634GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh,
635 const void *addr,
636 socklen_t addrlen)
637{
638 struct AddrEntry *ae;
639
640 if ((addrlen != sizeof(struct sockaddr_in)) &&
641 (addrlen != sizeof(struct sockaddr_in6)))
642 {
643 GNUNET_break (0);
644 return GNUNET_SYSERR;
645 }
646 for (ae = nh->ae_head; NULL != ae; ae = ae->next)
647 if ((addrlen == ae->addrlen) && (0 == memcmp (addr, &ae[1], addrlen)))
648 return GNUNET_YES;
649 return GNUNET_NO;
650}
651
652
653/**
654 * We learned about a peer (possibly behind NAT) so run the
655 * gnunet-nat-client to send dummy ICMP responses to cause
656 * that peer to connect to us (connection reversal).
657 *
658 * @param nh handle (used for configuration)
659 * @param local_sa our local address of the peer (IPv4-only)
660 * @param remote_sa the remote address of the peer (IPv4-only)
661 * @return #GNUNET_SYSERR on error,
662 * #GNUNET_NO if connection reversal is unavailable,
663 * #GNUNET_OK otherwise (presumably in progress)
664 */
665int
666GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh,
667 const struct sockaddr_in *local_sa,
668 const struct sockaddr_in *remote_sa)
669{
670 struct GNUNET_MQ_Envelope *env;
671 struct GNUNET_NAT_RequestConnectionReversalMessage *req;
672 char *buf;
673
674 if (NULL == nh->mq)
675 return GNUNET_SYSERR;
676 GNUNET_break (AF_INET == local_sa->sin_family);
677 GNUNET_break (AF_INET == remote_sa->sin_family);
678 env =
679 GNUNET_MQ_msg_extra (req,
680 2 * sizeof(struct sockaddr_in),
681 GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL);
682 req->local_addr_size = htons (sizeof(struct sockaddr_in));
683 req->remote_addr_size = htons (sizeof(struct sockaddr_in));
684 buf = (char *) &req[1];
685 GNUNET_memcpy (buf, local_sa, sizeof(struct sockaddr_in));
686 buf += sizeof(struct sockaddr_in);
687 GNUNET_memcpy (buf, remote_sa, sizeof(struct sockaddr_in));
688 GNUNET_MQ_send (nh->mq, env);
689 return GNUNET_OK;
690}
691
692
693/**
694 * Stop port redirection and public IP address detection for the given
695 * handle. This frees the handle, after having sent the needed
696 * commands to close open ports.
697 *
698 * @param nh the handle to stop
699 */
700void
701GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh)
702{
703 if (NULL != nh->mq)
704 {
705 GNUNET_MQ_destroy (nh->mq);
706 nh->mq = NULL;
707 }
708 if (NULL != nh->reconnect_task)
709 {
710 GNUNET_SCHEDULER_cancel (nh->reconnect_task);
711 nh->reconnect_task = NULL;
712 }
713 GNUNET_free (nh->reg);
714 GNUNET_free (nh);
715}
716
717
718/* end of nat_api.c */