diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-12-17 08:00:29 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-12-17 08:00:29 +0100 |
commit | 545faeda8f7fb7e03bb39602df6142f630157050 (patch) | |
tree | 26aac9f044212d5f492b4ac3d0522af136aab58e /src | |
parent | a1e76003ca590ea8d3e9387da35d87419417abb8 (diff) | |
download | gnunet-545faeda8f7fb7e03bb39602df6142f630157050.tar.gz gnunet-545faeda8f7fb7e03bb39602df6142f630157050.zip |
misc. improvements to new NAT service, starting with autoconfiguration logic
Diffstat (limited to 'src')
-rw-r--r-- | src/include/gnunet_nat_service.h | 2 | ||||
-rw-r--r-- | src/nat/Makefile.am | 1 | ||||
-rw-r--r-- | src/nat/gnunet-nat.c | 2 | ||||
-rw-r--r-- | src/nat/gnunet-service-nat.c | 899 | ||||
-rw-r--r-- | src/nat/gnunet-service-nat_mini.c | 759 | ||||
-rw-r--r-- | src/nat/gnunet-service-nat_mini.h | 127 | ||||
-rw-r--r-- | src/nat/nat.h | 2 | ||||
-rw-r--r-- | src/nat/nat_api.c | 7 | ||||
-rw-r--r-- | src/util/util.conf | 10 |
9 files changed, 1532 insertions, 277 deletions
diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h index b66b44240..a42d1d7e6 100644 --- a/src/include/gnunet_nat_service.h +++ b/src/include/gnunet_nat_service.h | |||
@@ -83,7 +83,7 @@ enum GNUNET_NAT_AddressClass | |||
83 | /** | 83 | /** |
84 | * Addresses useful in the local wired network, | 84 | * Addresses useful in the local wired network, |
85 | * i.e. a MAC. Sensitive, but obvious to people nearby. | 85 | * i.e. a MAC. Sensitive, but obvious to people nearby. |
86 | 86 | * | |
87 | * Useful for broadcasts. | 87 | * Useful for broadcasts. |
88 | */ | 88 | */ |
89 | GNUNET_NAT_AC_LAN = 8, | 89 | GNUNET_NAT_AC_LAN = 8, |
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index c62a8d2cf..1dd8e44b9 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am | |||
@@ -98,6 +98,7 @@ libgnunetnatnew_la_LDFLAGS = \ | |||
98 | gnunet_service_nat_SOURCES = \ | 98 | gnunet_service_nat_SOURCES = \ |
99 | gnunet-service-nat.c \ | 99 | gnunet-service-nat.c \ |
100 | gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ | 100 | gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ |
101 | gnunet-service-nat_mini.c gnunet-service-nat_mini.h \ | ||
101 | gnunet-service-nat_helper.c gnunet-service-nat_helper.h | 102 | gnunet-service-nat_helper.c gnunet-service-nat_helper.h |
102 | gnunet_service_nat_LDADD = \ | 103 | gnunet_service_nat_LDADD = \ |
103 | $(top_builddir)/src/util/libgnunetutil.la \ | 104 | $(top_builddir)/src/util/libgnunetutil.la \ |
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c index 10921150d..7d10167ce 100644 --- a/src/nat/gnunet-nat.c +++ b/src/nat/gnunet-nat.c | |||
@@ -611,7 +611,7 @@ main (int argc, | |||
611 | gettext_noop ("which external IP and port should be used to test"), | 611 | gettext_noop ("which external IP and port should be used to test"), |
612 | GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, | 612 | GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, |
613 | {'i', "in", "ADDRESS", | 613 | {'i', "in", "ADDRESS", |
614 | gettext_noop ("which IP and port are we locally using to listen to for connection reversals"), | 614 | gettext_noop ("which IP and port are we locally using to bind/listen to"), |
615 | GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr }, | 615 | GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr }, |
616 | {'r', "remote", "ADDRESS", | 616 | {'r', "remote", "ADDRESS", |
617 | gettext_noop ("which remote IP and port should be asked for connection reversal"), | 617 | gettext_noop ("which remote IP and port should be asked for connection reversal"), |
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c index 4ad6c8d2c..af1219dc0 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include "gnunet_statistics_service.h" | 40 | #include "gnunet_statistics_service.h" |
41 | #include "gnunet_nat_service.h" | 41 | #include "gnunet_nat_service.h" |
42 | #include "gnunet-service-nat_stun.h" | 42 | #include "gnunet-service-nat_stun.h" |
43 | #include "gnunet-service-nat_mini.h" | ||
43 | #include "gnunet-service-nat_helper.h" | 44 | #include "gnunet-service-nat_helper.h" |
44 | #include "nat.h" | 45 | #include "nat.h" |
45 | #include <gcrypt.h> | 46 | #include <gcrypt.h> |
@@ -51,6 +52,11 @@ | |||
51 | */ | 52 | */ |
52 | #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) | 53 | #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) |
53 | 54 | ||
55 | /** | ||
56 | * How long do we wait until we forcefully terminate autoconfiguration? | ||
57 | */ | ||
58 | #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
59 | |||
54 | 60 | ||
55 | /** | 61 | /** |
56 | * Internal data structure we track for each of our clients. | 62 | * Internal data structure we track for each of our clients. |
@@ -187,6 +193,75 @@ struct StunExternalIP | |||
187 | 193 | ||
188 | 194 | ||
189 | /** | 195 | /** |
196 | * Context for autoconfiguration operations. | ||
197 | */ | ||
198 | struct AutoconfigContext | ||
199 | { | ||
200 | /** | ||
201 | * Kept in a DLL. | ||
202 | */ | ||
203 | struct AutoconfigContext *prev; | ||
204 | |||
205 | /** | ||
206 | * Kept in a DLL. | ||
207 | */ | ||
208 | struct AutoconfigContext *next; | ||
209 | |||
210 | /** | ||
211 | * Which client asked the question. | ||
212 | */ | ||
213 | struct ClientHandle *ch; | ||
214 | |||
215 | /** | ||
216 | * Configuration we are creating. | ||
217 | */ | ||
218 | struct GNUNET_CONFIGURATION_Handle *c; | ||
219 | |||
220 | /** | ||
221 | * Timeout task to force termination. | ||
222 | */ | ||
223 | struct GNUNET_SCHEDULER_Task *timeout_task; | ||
224 | |||
225 | /** | ||
226 | * What type of system are we on? | ||
227 | */ | ||
228 | char *system_type; | ||
229 | |||
230 | /** | ||
231 | * Handle to activity to probe for our external IP. | ||
232 | */ | ||
233 | struct GNUNET_NAT_ExternalHandle *probe_external; | ||
234 | |||
235 | /** | ||
236 | * #GNUNET_YES if upnpc should be used, | ||
237 | * #GNUNET_NO if upnpc should not be used, | ||
238 | * #GNUNET_SYSERR if we should simply not change the option. | ||
239 | */ | ||
240 | int enable_upnpc; | ||
241 | |||
242 | /** | ||
243 | * Status code to return to the client. | ||
244 | */ | ||
245 | enum GNUNET_NAT_StatusCode status_code; | ||
246 | |||
247 | /** | ||
248 | * NAT type to return to the client. | ||
249 | */ | ||
250 | enum GNUNET_NAT_Type type; | ||
251 | }; | ||
252 | |||
253 | |||
254 | /** | ||
255 | * DLL of our autoconfiguration operations. | ||
256 | */ | ||
257 | static struct AutoconfigContext *ac_head; | ||
258 | |||
259 | /** | ||
260 | * DLL of our autoconfiguration operations. | ||
261 | */ | ||
262 | static struct AutoconfigContext *ac_tail; | ||
263 | |||
264 | /** | ||
190 | * Timeout to use when STUN data is considered stale. | 265 | * Timeout to use when STUN data is considered stale. |
191 | */ | 266 | */ |
192 | static struct GNUNET_TIME_Relative stun_stale_timeout; | 267 | static struct GNUNET_TIME_Relative stun_stale_timeout; |
@@ -236,6 +311,12 @@ static struct StunExternalIP *se_head; | |||
236 | */ | 311 | */ |
237 | static struct StunExternalIP *se_tail; | 312 | static struct StunExternalIP *se_tail; |
238 | 313 | ||
314 | /** | ||
315 | * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, | ||
316 | * #GNUNET_SYSERR if configuration enabled but binary is unavailable. | ||
317 | */ | ||
318 | static int enable_upnp; | ||
319 | |||
239 | 320 | ||
240 | /** | 321 | /** |
241 | * Free the DLL starting at #lal_head. | 322 | * Free the DLL starting at #lal_head. |
@@ -322,7 +403,9 @@ match_ipv4 (const char *network, | |||
322 | uint8_t bits) | 403 | uint8_t bits) |
323 | { | 404 | { |
324 | struct in_addr net; | 405 | struct in_addr net; |
325 | 406 | ||
407 | if (0 == ip->s_addr) | ||
408 | return GNUNET_YES; | ||
326 | if (0 == bits) | 409 | if (0 == bits) |
327 | return GNUNET_YES; | 410 | return GNUNET_YES; |
328 | GNUNET_assert (1 == inet_pton (AF_INET, | 411 | GNUNET_assert (1 == inet_pton (AF_INET, |
@@ -355,6 +438,10 @@ match_ipv6 (const char *network, | |||
355 | network, | 438 | network, |
356 | &net)); | 439 | &net)); |
357 | memset (&mask, 0, sizeof (mask)); | 440 | memset (&mask, 0, sizeof (mask)); |
441 | if (0 == memcmp (&mask, | ||
442 | ip, | ||
443 | sizeof (mask))) | ||
444 | return GNUNET_YES; | ||
358 | off = 0; | 445 | off = 0; |
359 | while (bits > 8) | 446 | while (bits > 8) |
360 | { | 447 | { |
@@ -411,6 +498,296 @@ is_nat_v6 (const struct in6_addr *ip) | |||
411 | 498 | ||
412 | 499 | ||
413 | /** | 500 | /** |
501 | * Closure for #ifc_proc. | ||
502 | */ | ||
503 | struct IfcProcContext | ||
504 | { | ||
505 | |||
506 | /** | ||
507 | * Head of DLL of local addresses. | ||
508 | */ | ||
509 | struct LocalAddressList *lal_head; | ||
510 | |||
511 | /** | ||
512 | * Tail of DLL of local addresses. | ||
513 | */ | ||
514 | struct LocalAddressList *lal_tail; | ||
515 | |||
516 | }; | ||
517 | |||
518 | |||
519 | /** | ||
520 | * Callback function invoked for each interface found. Adds them | ||
521 | * to our new address list. | ||
522 | * | ||
523 | * @param cls a `struct IfcProcContext *` | ||
524 | * @param name name of the interface (can be NULL for unknown) | ||
525 | * @param isDefault is this presumably the default interface | ||
526 | * @param addr address of this interface (can be NULL for unknown or unassigned) | ||
527 | * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) | ||
528 | * @param netmask the network mask (can be NULL for unknown or unassigned) | ||
529 | * @param addrlen length of the address | ||
530 | * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort | ||
531 | */ | ||
532 | static int | ||
533 | ifc_proc (void *cls, | ||
534 | const char *name, | ||
535 | int isDefault, | ||
536 | const struct sockaddr *addr, | ||
537 | const struct sockaddr *broadcast_addr, | ||
538 | const struct sockaddr *netmask, | ||
539 | socklen_t addrlen) | ||
540 | { | ||
541 | struct IfcProcContext *ifc_ctx = cls; | ||
542 | struct LocalAddressList *lal; | ||
543 | size_t alen; | ||
544 | const struct in_addr *ip4; | ||
545 | const struct in6_addr *ip6; | ||
546 | enum GNUNET_NAT_AddressClass ac; | ||
547 | |||
548 | switch (addr->sa_family) | ||
549 | { | ||
550 | case AF_INET: | ||
551 | alen = sizeof (struct sockaddr_in); | ||
552 | ip4 = &((const struct sockaddr_in *) addr)->sin_addr; | ||
553 | if (match_ipv4 ("127.0.0.0", ip4, 8)) | ||
554 | ac = GNUNET_NAT_AC_LOOPBACK; | ||
555 | else if (is_nat_v4 (ip4)) | ||
556 | ac = GNUNET_NAT_AC_LAN; | ||
557 | else | ||
558 | ac = GNUNET_NAT_AC_GLOBAL; | ||
559 | break; | ||
560 | case AF_INET6: | ||
561 | alen = sizeof (struct sockaddr_in6); | ||
562 | ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; | ||
563 | if (match_ipv6 ("::1", ip6, 128)) | ||
564 | ac = GNUNET_NAT_AC_LOOPBACK; | ||
565 | else if (is_nat_v6 (ip6)) | ||
566 | ac = GNUNET_NAT_AC_LAN; | ||
567 | else | ||
568 | ac = GNUNET_NAT_AC_GLOBAL; | ||
569 | if ( (ip6->s6_addr[11] == 0xFF) && | ||
570 | (ip6->s6_addr[12] == 0xFE) ) | ||
571 | { | ||
572 | /* contains a MAC, be extra careful! */ | ||
573 | ac |= GNUNET_NAT_AC_PRIVATE; | ||
574 | } | ||
575 | break; | ||
576 | #if AF_UNIX | ||
577 | case AF_UNIX: | ||
578 | GNUNET_break (0); | ||
579 | return GNUNET_OK; | ||
580 | #endif | ||
581 | default: | ||
582 | GNUNET_break (0); | ||
583 | return GNUNET_OK; | ||
584 | } | ||
585 | lal = GNUNET_malloc (sizeof (*lal)); | ||
586 | lal->af = addr->sa_family; | ||
587 | lal->ac = ac; | ||
588 | GNUNET_memcpy (&lal->addr, | ||
589 | addr, | ||
590 | alen); | ||
591 | GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head, | ||
592 | ifc_ctx->lal_tail, | ||
593 | lal); | ||
594 | return GNUNET_OK; | ||
595 | } | ||
596 | |||
597 | |||
598 | /** | ||
599 | * Notify client about a change in the list of addresses this peer | ||
600 | * has. | ||
601 | * | ||
602 | * @param delta the entry in the list that changed | ||
603 | * @param ch client to contact | ||
604 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
605 | * @param addr the address that changed | ||
606 | * @param addr_len number of bytes in @a addr | ||
607 | */ | ||
608 | static void | ||
609 | notify_client (struct LocalAddressList *delta, | ||
610 | struct ClientHandle *ch, | ||
611 | int add, | ||
612 | const void *addr, | ||
613 | size_t addr_len) | ||
614 | { | ||
615 | struct GNUNET_MQ_Envelope *env; | ||
616 | struct GNUNET_NAT_AddressChangeNotificationMessage *msg; | ||
617 | |||
618 | env = GNUNET_MQ_msg_extra (msg, | ||
619 | addr_len, | ||
620 | GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); | ||
621 | msg->add_remove = htonl (add); | ||
622 | msg->addr_class = htonl (delta->ac); | ||
623 | GNUNET_memcpy (&msg[1], | ||
624 | addr, | ||
625 | addr_len); | ||
626 | GNUNET_MQ_send (ch->mq, | ||
627 | env); | ||
628 | } | ||
629 | |||
630 | |||
631 | /** | ||
632 | * Check if we should bother to notify this client about this | ||
633 | * address change, and if so, do it. | ||
634 | * | ||
635 | * @param delta the entry in the list that changed | ||
636 | * @param ch client to check | ||
637 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
638 | */ | ||
639 | static void | ||
640 | check_notify_client (struct LocalAddressList *delta, | ||
641 | struct ClientHandle *ch, | ||
642 | int add) | ||
643 | { | ||
644 | size_t alen; | ||
645 | struct sockaddr_in v4; | ||
646 | struct sockaddr_in6 v6; | ||
647 | |||
648 | if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) | ||
649 | return; | ||
650 | switch (delta->af) | ||
651 | { | ||
652 | case AF_INET: | ||
653 | alen = sizeof (struct sockaddr_in); | ||
654 | GNUNET_memcpy (&v4, | ||
655 | &delta->addr, | ||
656 | alen); | ||
657 | for (unsigned int i=0;i<ch->num_addrs;i++) | ||
658 | { | ||
659 | const struct sockaddr_in *c4; | ||
660 | |||
661 | if (AF_INET != ch->addrs[i]->sa_family) | ||
662 | return; /* IPv4 not relevant */ | ||
663 | c4 = (const struct sockaddr_in *) ch->addrs[i]; | ||
664 | v4.sin_port = c4->sin_port; | ||
665 | notify_client (delta, | ||
666 | ch, | ||
667 | add, | ||
668 | &v4, | ||
669 | alen); | ||
670 | } | ||
671 | break; | ||
672 | case AF_INET6: | ||
673 | alen = sizeof (struct sockaddr_in6); | ||
674 | GNUNET_memcpy (&v6, | ||
675 | &delta->addr, | ||
676 | alen); | ||
677 | for (unsigned int i=0;i<ch->num_addrs;i++) | ||
678 | { | ||
679 | const struct sockaddr_in6 *c6; | ||
680 | |||
681 | if (AF_INET6 != ch->addrs[i]->sa_family) | ||
682 | return; /* IPv4 not relevant */ | ||
683 | c6 = (const struct sockaddr_in6 *) ch->addrs[i]; | ||
684 | v6.sin6_port = c6->sin6_port; | ||
685 | notify_client (delta, | ||
686 | ch, | ||
687 | add, | ||
688 | &v6, | ||
689 | alen); | ||
690 | } | ||
691 | break; | ||
692 | default: | ||
693 | GNUNET_break (0); | ||
694 | return; | ||
695 | } | ||
696 | } | ||
697 | |||
698 | |||
699 | /** | ||
700 | * Notify all clients about a change in the list | ||
701 | * of addresses this peer has. | ||
702 | * | ||
703 | * @param delta the entry in the list that changed | ||
704 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
705 | */ | ||
706 | static void | ||
707 | notify_clients (struct LocalAddressList *delta, | ||
708 | int add) | ||
709 | { | ||
710 | for (struct ClientHandle *ch = ch_head; | ||
711 | NULL != ch; | ||
712 | ch = ch->next) | ||
713 | check_notify_client (delta, | ||
714 | ch, | ||
715 | add); | ||
716 | } | ||
717 | |||
718 | |||
719 | /** | ||
720 | * Task we run periodically to scan for network interfaces. | ||
721 | * | ||
722 | * @param cls NULL | ||
723 | */ | ||
724 | static void | ||
725 | run_scan (void *cls) | ||
726 | { | ||
727 | struct IfcProcContext ifc_ctx; | ||
728 | int found; | ||
729 | |||
730 | scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, | ||
731 | &run_scan, | ||
732 | NULL); | ||
733 | memset (&ifc_ctx, | ||
734 | 0, | ||
735 | sizeof (ifc_ctx)); | ||
736 | GNUNET_OS_network_interfaces_list (&ifc_proc, | ||
737 | &ifc_ctx); | ||
738 | /* remove addresses that disappeared */ | ||
739 | for (struct LocalAddressList *lal = lal_head; | ||
740 | NULL != lal; | ||
741 | lal = lal->next) | ||
742 | { | ||
743 | found = GNUNET_NO; | ||
744 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | ||
745 | NULL != pos; | ||
746 | pos = pos->next) | ||
747 | { | ||
748 | if ( (pos->af == lal->af) && | ||
749 | (0 == memcmp (&lal->addr, | ||
750 | &pos->addr, | ||
751 | (AF_INET == lal->af) | ||
752 | ? sizeof (struct sockaddr_in) | ||
753 | : sizeof (struct sockaddr_in6))) ) | ||
754 | found = GNUNET_YES; | ||
755 | } | ||
756 | if (GNUNET_NO == found) | ||
757 | notify_clients (lal, | ||
758 | GNUNET_NO); | ||
759 | } | ||
760 | |||
761 | /* add addresses that appeared */ | ||
762 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | ||
763 | NULL != pos; | ||
764 | pos = pos->next) | ||
765 | { | ||
766 | found = GNUNET_NO; | ||
767 | for (struct LocalAddressList *lal = lal_head; | ||
768 | NULL != lal; | ||
769 | lal = lal->next) | ||
770 | { | ||
771 | if ( (pos->af == lal->af) && | ||
772 | (0 == memcmp (&lal->addr, | ||
773 | &pos->addr, | ||
774 | (AF_INET == lal->af) | ||
775 | ? sizeof (struct sockaddr_in) | ||
776 | : sizeof (struct sockaddr_in6))) ) | ||
777 | found = GNUNET_YES; | ||
778 | } | ||
779 | if (GNUNET_NO == found) | ||
780 | notify_clients (pos, | ||
781 | GNUNET_YES); | ||
782 | } | ||
783 | |||
784 | destroy_lal (); | ||
785 | lal_head = ifc_ctx.lal_head; | ||
786 | lal_tail = ifc_ctx.lal_tail; | ||
787 | } | ||
788 | |||
789 | |||
790 | /** | ||
414 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. | 791 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. |
415 | * We remember the client for updates upon future NAT events. | 792 | * We remember the client for updates upon future NAT events. |
416 | * | 793 | * |
@@ -438,7 +815,7 @@ handle_register (void *cls, | |||
438 | ch->flags = message->flags; | 815 | ch->flags = message->flags; |
439 | ch->proto = message->proto; | 816 | ch->proto = message->proto; |
440 | ch->adv_port = ntohs (message->adv_port); | 817 | ch->adv_port = ntohs (message->adv_port); |
441 | ch->num_addrs = ntohs (message->adv_port); | 818 | ch->num_addrs = ntohs (message->num_addrs); |
442 | ch->addrs = GNUNET_new_array (ch->num_addrs, | 819 | ch->addrs = GNUNET_new_array (ch->num_addrs, |
443 | struct sockaddr *); | 820 | struct sockaddr *); |
444 | left = ntohs (message->header.size) - sizeof (*message); | 821 | left = ntohs (message->header.size) - sizeof (*message); |
@@ -490,7 +867,16 @@ handle_register (void *cls, | |||
490 | sa, | 867 | sa, |
491 | alen); | 868 | alen); |
492 | off += alen; | 869 | off += alen; |
493 | } | 870 | } |
871 | /* Actually send IP address list to client */ | ||
872 | for (struct LocalAddressList *lal = lal_head; | ||
873 | NULL != lal; | ||
874 | lal = lal->next) | ||
875 | { | ||
876 | check_notify_client (lal, | ||
877 | ch, | ||
878 | GNUNET_YES); | ||
879 | } | ||
494 | GNUNET_SERVICE_client_continue (ch->client); | 880 | GNUNET_SERVICE_client_continue (ch->client); |
495 | } | 881 | } |
496 | 882 | ||
@@ -826,344 +1212,289 @@ check_autoconfig_request (void *cls, | |||
826 | 1212 | ||
827 | 1213 | ||
828 | /** | 1214 | /** |
829 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from | 1215 | * Stop all pending activities with respect to the @a ac |
830 | * client. | ||
831 | * | 1216 | * |
832 | * @param cls client who sent the message | 1217 | * @param ac autoconfiguration to terminate activities for |
833 | * @param message the message received | ||
834 | */ | 1218 | */ |
835 | static void | 1219 | static void |
836 | handle_autoconfig_request (void *cls, | 1220 | terminate_ac_activities (struct AutoconfigContext *ac) |
837 | const struct GNUNET_NAT_AutoconfigRequestMessage *message) | ||
838 | { | 1221 | { |
839 | struct ClientHandle *ch = cls; | 1222 | if (NULL != ac->probe_external) |
840 | size_t left = ntohs (message->header.size) - sizeof (*message); | ||
841 | struct GNUNET_CONFIGURATION_Handle *c; | ||
842 | |||
843 | c = GNUNET_CONFIGURATION_create (); | ||
844 | if (GNUNET_OK != | ||
845 | GNUNET_CONFIGURATION_deserialize (c, | ||
846 | (const char *) &message[1], | ||
847 | left, | ||
848 | GNUNET_NO)) | ||
849 | { | 1223 | { |
850 | GNUNET_break (0); | 1224 | GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external); |
851 | GNUNET_SERVICE_client_drop (ch->client); | 1225 | ac->probe_external = NULL; |
852 | return; | 1226 | } |
1227 | if (NULL != ac->timeout_task) | ||
1228 | { | ||
1229 | GNUNET_SCHEDULER_cancel (ac->timeout_task); | ||
1230 | ac->timeout_task = NULL; | ||
853 | } | 1231 | } |
854 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
855 | "Received REQUEST_AUTO_CONFIG message from client\n"); | ||
856 | // FIXME: actually handle request... | ||
857 | GNUNET_CONFIGURATION_destroy (c); | ||
858 | GNUNET_SERVICE_client_continue (ch->client); | ||
859 | } | 1232 | } |
860 | 1233 | ||
861 | 1234 | ||
862 | /** | 1235 | /** |
863 | * Task run during shutdown. | 1236 | * Finish handling the autoconfiguration request and send |
1237 | * the response to the client. | ||
864 | * | 1238 | * |
865 | * @param cls unused | 1239 | * @param cls the `struct AutoconfigContext` to conclude |
866 | */ | 1240 | */ |
867 | static void | 1241 | static void |
868 | shutdown_task (void *cls) | 1242 | conclude_autoconfig_request (void *cls) |
869 | { | 1243 | { |
870 | struct StunExternalIP *se; | 1244 | struct AutoconfigContext *ac = cls; |
1245 | struct ClientHandle *ch = ac->ch; | ||
1246 | struct GNUNET_NAT_AutoconfigResultMessage *arm; | ||
1247 | struct GNUNET_MQ_Envelope *env; | ||
1248 | size_t c_size; | ||
1249 | char *buf; | ||
1250 | |||
1251 | ac->timeout_task = NULL; | ||
1252 | terminate_ac_activities (ac); | ||
1253 | |||
1254 | /* Send back response */ | ||
1255 | buf = GNUNET_CONFIGURATION_serialize (ac->c, | ||
1256 | &c_size); | ||
1257 | env = GNUNET_MQ_msg_extra (arm, | ||
1258 | c_size, | ||
1259 | GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT); | ||
1260 | arm->status_code = htonl ((uint32_t) ac->status_code); | ||
1261 | arm->type = htonl ((uint32_t) ac->type); | ||
1262 | GNUNET_memcpy (&arm[1], | ||
1263 | buf, | ||
1264 | c_size); | ||
1265 | GNUNET_free (buf); | ||
1266 | GNUNET_MQ_send (ch->mq, | ||
1267 | env); | ||
871 | 1268 | ||
872 | while (NULL != (se = se_head)) | 1269 | /* clean up */ |
873 | { | 1270 | GNUNET_free (ac->system_type); |
874 | GNUNET_CONTAINER_DLL_remove (se_head, | 1271 | GNUNET_CONFIGURATION_destroy (ac->c); |
875 | se_tail, | 1272 | GNUNET_CONTAINER_DLL_remove (ac_head, |
876 | se); | 1273 | ac_tail, |
877 | GNUNET_SCHEDULER_cancel (se->timeout_task); | 1274 | ac); |
878 | GNUNET_free (se); | 1275 | GNUNET_free (ac); |
879 | } | 1276 | GNUNET_SERVICE_client_continue (ch->client); |
880 | if (NULL != scan_task) | ||
881 | { | ||
882 | GNUNET_SCHEDULER_cancel (scan_task); | ||
883 | scan_task = NULL; | ||
884 | } | ||
885 | if (NULL != stats) | ||
886 | { | ||
887 | GNUNET_STATISTICS_destroy (stats, GNUNET_NO); | ||
888 | stats = NULL; | ||
889 | } | ||
890 | destroy_lal (); | ||
891 | } | 1277 | } |
892 | 1278 | ||
893 | 1279 | ||
894 | /** | 1280 | /** |
895 | * Closure for #ifc_proc. | 1281 | * Check if all autoconfiguration operations have concluded, |
1282 | * and if they have, send the result back to the client. | ||
1283 | * | ||
1284 | * @param ac autoconfiguation context to check | ||
896 | */ | 1285 | */ |
897 | struct IfcProcContext | 1286 | static void |
1287 | check_autoconfig_finished (struct AutoconfigContext *ac) | ||
898 | { | 1288 | { |
899 | 1289 | if (NULL != ac->probe_external) | |
900 | /** | 1290 | return; |
901 | * Head of DLL of local addresses. | 1291 | GNUNET_SCHEDULER_cancel (ac->timeout_task); |
902 | */ | 1292 | ac->timeout_task |
903 | struct LocalAddressList *lal_head; | 1293 | = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request, |
904 | 1294 | ac); | |
905 | /** | 1295 | } |
906 | * Tail of DLL of local addresses. | ||
907 | */ | ||
908 | struct LocalAddressList *lal_tail; | ||
909 | |||
910 | }; | ||
911 | 1296 | ||
912 | 1297 | ||
913 | /** | 1298 | /** |
914 | * Callback function invoked for each interface found. Adds them | 1299 | * Update ENABLE_UPNPC configuration option. |
915 | * to our new address list. | ||
916 | * | 1300 | * |
917 | * @param cls a `struct IfcProcContext *` | 1301 | * @param ac autoconfiguration to update |
918 | * @param name name of the interface (can be NULL for unknown) | ||
919 | * @param isDefault is this presumably the default interface | ||
920 | * @param addr address of this interface (can be NULL for unknown or unassigned) | ||
921 | * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) | ||
922 | * @param netmask the network mask (can be NULL for unknown or unassigned) | ||
923 | * @param addrlen length of the address | ||
924 | * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort | ||
925 | */ | 1302 | */ |
926 | static int | 1303 | static void |
927 | ifc_proc (void *cls, | 1304 | update_enable_upnpc_option (struct AutoconfigContext *ac) |
928 | const char *name, | ||
929 | int isDefault, | ||
930 | const struct sockaddr *addr, | ||
931 | const struct sockaddr *broadcast_addr, | ||
932 | const struct sockaddr *netmask, | ||
933 | socklen_t addrlen) | ||
934 | { | 1305 | { |
935 | struct IfcProcContext *ifc_ctx = cls; | 1306 | switch (ac->enable_upnpc) |
936 | struct LocalAddressList *lal; | ||
937 | size_t alen; | ||
938 | const struct in_addr *ip4; | ||
939 | const struct in6_addr *ip6; | ||
940 | enum GNUNET_NAT_AddressClass ac; | ||
941 | |||
942 | switch (addr->sa_family) | ||
943 | { | 1307 | { |
944 | case AF_INET: | 1308 | case GNUNET_YES: |
945 | alen = sizeof (struct sockaddr_in); | 1309 | GNUNET_CONFIGURATION_set_value_string (ac->c, |
946 | ip4 = &((const struct sockaddr_in *) addr)->sin_addr; | 1310 | "NAT", |
947 | if (match_ipv4 ("127.0.0.0", ip4, 8)) | 1311 | "ENABLE_UPNPC", |
948 | ac = GNUNET_NAT_AC_LOOPBACK; | 1312 | "YES"); |
949 | else if (is_nat_v4 (ip4)) | ||
950 | ac = GNUNET_NAT_AC_LAN; | ||
951 | else | ||
952 | ac = GNUNET_NAT_AC_GLOBAL; | ||
953 | break; | 1313 | break; |
954 | case AF_INET6: | 1314 | case GNUNET_NO: |
955 | alen = sizeof (struct sockaddr_in6); | 1315 | GNUNET_CONFIGURATION_set_value_string (ac->c, |
956 | ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; | 1316 | "NAT", |
957 | if (match_ipv6 ("::1", ip6, 128)) | 1317 | "ENABLE_UPNPC", |
958 | ac = GNUNET_NAT_AC_LOOPBACK; | 1318 | "NO"); |
959 | else if (is_nat_v6 (ip6)) | 1319 | break; |
960 | ac = GNUNET_NAT_AC_LAN; | 1320 | case GNUNET_SYSERR: |
961 | else | 1321 | /* We are unsure, do not change option */ |
962 | ac = GNUNET_NAT_AC_GLOBAL; | ||
963 | if ( (ip6->s6_addr[11] == 0xFF) && | ||
964 | (ip6->s6_addr[12] == 0xFE) ) | ||
965 | { | ||
966 | /* contains a MAC, be extra careful! */ | ||
967 | ac |= GNUNET_NAT_AC_PRIVATE; | ||
968 | } | ||
969 | break; | 1322 | break; |
970 | #if AF_UNIX | ||
971 | case AF_UNIX: | ||
972 | GNUNET_break (0); | ||
973 | return GNUNET_OK; | ||
974 | #endif | ||
975 | default: | ||
976 | GNUNET_break (0); | ||
977 | return GNUNET_OK; | ||
978 | } | 1323 | } |
979 | lal = GNUNET_malloc (sizeof (*lal)); | ||
980 | lal->af = addr->sa_family; | ||
981 | lal->ac = ac; | ||
982 | GNUNET_memcpy (&lal->addr, | ||
983 | addr, | ||
984 | alen); | ||
985 | GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head, | ||
986 | ifc_ctx->lal_tail, | ||
987 | lal); | ||
988 | return GNUNET_OK; | ||
989 | } | 1324 | } |
990 | 1325 | ||
991 | 1326 | ||
992 | /** | 1327 | /** |
993 | * Notify client about a change in the list | 1328 | * Handle result from external IP address probe during |
994 | * of addresses this peer has. | 1329 | * autoconfiguration. |
995 | * | 1330 | * |
996 | * @param delta the entry in the list that changed | 1331 | * @param cls our `struct AutoconfigContext` |
997 | * @param ch client to contact | 1332 | * @param addr the address, NULL on errors |
998 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | 1333 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code |
999 | * @param addr the address that changed | ||
1000 | * @param addr_len number of bytes in @a addr | ||
1001 | */ | 1334 | */ |
1002 | static void | 1335 | static void |
1003 | notify_client (struct LocalAddressList *delta, | 1336 | auto_external_result_cb (void *cls, |
1004 | struct ClientHandle *ch, | 1337 | const struct in_addr *addr, |
1005 | int add, | 1338 | enum GNUNET_NAT_StatusCode result) |
1006 | const void *addr, | ||
1007 | size_t addr_len) | ||
1008 | { | 1339 | { |
1009 | struct GNUNET_MQ_Envelope *env; | 1340 | struct AutoconfigContext *ac = cls; |
1010 | struct GNUNET_NAT_AddressChangeNotificationMessage *msg; | ||
1011 | 1341 | ||
1012 | env = GNUNET_MQ_msg_extra (msg, | 1342 | ac->probe_external = NULL; |
1013 | addr_len, | 1343 | switch (result) |
1014 | GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); | 1344 | { |
1015 | msg->add_remove = htonl (add); | 1345 | case GNUNET_NAT_ERROR_SUCCESS: |
1016 | msg->addr_class = htonl (delta->ac); | 1346 | ac->enable_upnpc = GNUNET_YES; |
1017 | GNUNET_memcpy (&msg[1], | 1347 | break; |
1018 | addr, | 1348 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: |
1019 | addr_len); | 1349 | case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: |
1020 | GNUNET_MQ_send (ch->mq, | 1350 | case GNUNET_NAT_ERROR_IPC_FAILURE: |
1021 | env); | 1351 | ac->enable_upnpc = GNUNET_NO; /* did not work */ |
1022 | } | 1352 | break; |
1353 | default: | ||
1354 | GNUNET_break (0); /* unexpected */ | ||
1355 | ac->enable_upnpc = GNUNET_SYSERR; | ||
1356 | break; | ||
1357 | } | ||
1358 | update_enable_upnpc_option (ac); | ||
1359 | check_autoconfig_finished (ac); | ||
1360 | } | ||
1023 | 1361 | ||
1024 | 1362 | ||
1025 | /** | 1363 | /** |
1026 | * Notify all clients about a change in the list | 1364 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from |
1027 | * of addresses this peer has. | 1365 | * client. |
1028 | * | 1366 | * |
1029 | * @param delta the entry in the list that changed | 1367 | * @param cls client who sent the message |
1030 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | 1368 | * @param message the message received |
1031 | */ | 1369 | */ |
1032 | static void | 1370 | static void |
1033 | notify_clients (struct LocalAddressList *delta, | 1371 | handle_autoconfig_request (void *cls, |
1034 | int add) | 1372 | const struct GNUNET_NAT_AutoconfigRequestMessage *message) |
1035 | { | 1373 | { |
1036 | for (struct ClientHandle *ch = ch_head; | 1374 | struct ClientHandle *ch = cls; |
1037 | NULL != ch; | 1375 | size_t left = ntohs (message->header.size) - sizeof (*message); |
1038 | ch = ch->next) | 1376 | struct LocalAddressList *lal; |
1377 | struct AutoconfigContext *ac; | ||
1378 | |||
1379 | ac = GNUNET_new (struct AutoconfigContext); | ||
1380 | ac->c = GNUNET_CONFIGURATION_create (); | ||
1381 | if (GNUNET_OK != | ||
1382 | GNUNET_CONFIGURATION_deserialize (ac->c, | ||
1383 | (const char *) &message[1], | ||
1384 | left, | ||
1385 | GNUNET_NO)) | ||
1039 | { | 1386 | { |
1040 | size_t alen; | 1387 | GNUNET_break (0); |
1041 | struct sockaddr_in v4; | 1388 | GNUNET_SERVICE_client_drop (ch->client); |
1042 | struct sockaddr_in6 v6; | 1389 | GNUNET_CONFIGURATION_destroy (ac->c); |
1043 | 1390 | GNUNET_free (ac); | |
1044 | if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) | 1391 | return; |
1045 | continue; | 1392 | } |
1046 | switch (delta->af) | 1393 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
1394 | "Received REQUEST_AUTO_CONFIG message from client\n"); | ||
1395 | |||
1396 | if (GNUNET_OK != | ||
1397 | GNUNET_CONFIGURATION_get_value_string (ac->c, | ||
1398 | "PEER", | ||
1399 | "SYSTEM_TYPE", | ||
1400 | &ac->system_type)) | ||
1401 | ac->system_type = "UNKNOWN"; | ||
1402 | |||
1403 | GNUNET_CONTAINER_DLL_insert (ac_head, | ||
1404 | ac_tail, | ||
1405 | ac); | ||
1406 | ac->timeout_task | ||
1407 | = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT, | ||
1408 | &conclude_autoconfig_request, | ||
1409 | ac); | ||
1410 | ac->enable_upnpc = GNUNET_SYSERR; /* undecided */ | ||
1411 | |||
1412 | /* Probe for upnpc */ | ||
1413 | if (GNUNET_SYSERR == | ||
1414 | GNUNET_OS_check_helper_binary ("upnpc", | ||
1415 | GNUNET_NO, | ||
1416 | NULL)) | ||
1417 | { | ||
1418 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1419 | _("UPnP client `upnpc` command not found, disabling UPnP\n")); | ||
1420 | ac->enable_upnpc = GNUNET_NO; | ||
1421 | } | ||
1422 | else | ||
1423 | { | ||
1424 | for (lal = lal_head; NULL != lal; lal = lal->next) | ||
1425 | if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN)) | ||
1426 | /* we are behind NAT, useful to try upnpc */ | ||
1427 | ac->enable_upnpc = GNUNET_YES; | ||
1428 | } | ||
1429 | if (GNUNET_YES == ac->enable_upnpc) | ||
1430 | { | ||
1431 | /* If we are a mobile device, always leave it on as the network | ||
1432 | may change to one that supports UPnP anytime. If we are | ||
1433 | stationary, check if our network actually supports UPnP, and if | ||
1434 | not, disable it. */ | ||
1435 | if ( (0 == strcasecmp (ac->system_type, | ||
1436 | "INFRASTRUCTURE")) || | ||
1437 | (0 == strcasecmp (ac->system_type, | ||
1438 | "DESKTOP")) ) | ||
1047 | { | 1439 | { |
1048 | case AF_INET: | 1440 | /* Check if upnpc gives us an external IP */ |
1049 | alen = sizeof (struct sockaddr_in); | 1441 | ac->probe_external |
1050 | GNUNET_memcpy (&v4, | 1442 | = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb, |
1051 | &delta->addr, | 1443 | ac); |
1052 | alen); | ||
1053 | for (unsigned int i=0;i<ch->num_addrs;i++) | ||
1054 | { | ||
1055 | const struct sockaddr_in *c4; | ||
1056 | |||
1057 | if (AF_INET != ch->addrs[i]->sa_family) | ||
1058 | continue; /* IPv4 not relevant */ | ||
1059 | c4 = (const struct sockaddr_in *) ch->addrs[i]; | ||
1060 | v4.sin_port = c4->sin_port; | ||
1061 | notify_client (delta, | ||
1062 | ch, | ||
1063 | add, | ||
1064 | &v4, | ||
1065 | alen); | ||
1066 | } | ||
1067 | break; | ||
1068 | case AF_INET6: | ||
1069 | alen = sizeof (struct sockaddr_in6); | ||
1070 | GNUNET_memcpy (&v6, | ||
1071 | &delta->addr, | ||
1072 | alen); | ||
1073 | for (unsigned int i=0;i<ch->num_addrs;i++) | ||
1074 | { | ||
1075 | const struct sockaddr_in6 *c6; | ||
1076 | |||
1077 | if (AF_INET6 != ch->addrs[i]->sa_family) | ||
1078 | continue; /* IPv4 not relevant */ | ||
1079 | c6 = (const struct sockaddr_in6 *) ch->addrs[i]; | ||
1080 | v6.sin6_port = c6->sin6_port; | ||
1081 | notify_client (delta, | ||
1082 | ch, | ||
1083 | add, | ||
1084 | &v6, | ||
1085 | alen); | ||
1086 | } | ||
1087 | break; | ||
1088 | default: | ||
1089 | GNUNET_break (0); | ||
1090 | continue; | ||
1091 | } | 1444 | } |
1092 | } | 1445 | } |
1446 | if (NULL == ac->probe_external) | ||
1447 | update_enable_upnpc_option (ac); | ||
1448 | |||
1449 | /* Finally, check if we are already done */ | ||
1450 | check_autoconfig_finished (ac); | ||
1093 | } | 1451 | } |
1094 | 1452 | ||
1095 | 1453 | ||
1096 | /** | 1454 | /** |
1097 | * Task we run periodically to scan for network interfaces. | 1455 | * Task run during shutdown. |
1098 | * | 1456 | * |
1099 | * @param cls NULL | 1457 | * @param cls unused |
1100 | */ | 1458 | */ |
1101 | static void | 1459 | static void |
1102 | run_scan (void *cls) | 1460 | shutdown_task (void *cls) |
1103 | { | 1461 | { |
1104 | struct IfcProcContext ifc_ctx; | 1462 | struct StunExternalIP *se; |
1105 | int found; | 1463 | struct AutoconfigContext *ac; |
1106 | 1464 | ||
1107 | scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, | 1465 | while (NULL != (ac = ac_head)) |
1108 | &run_scan, | ||
1109 | NULL); | ||
1110 | memset (&ifc_ctx, | ||
1111 | 0, | ||
1112 | sizeof (ifc_ctx)); | ||
1113 | GNUNET_OS_network_interfaces_list (&ifc_proc, | ||
1114 | &ifc_ctx); | ||
1115 | for (struct LocalAddressList *lal = lal_head; | ||
1116 | NULL != lal; | ||
1117 | lal = lal->next) | ||
1118 | { | 1466 | { |
1119 | found = GNUNET_NO; | 1467 | GNUNET_CONTAINER_DLL_remove (ac_head, |
1120 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | 1468 | ac_tail, |
1121 | NULL != pos; | 1469 | ac); |
1122 | pos = pos->next) | 1470 | terminate_ac_activities (ac); |
1123 | { | 1471 | GNUNET_free (ac); |
1124 | if ( (pos->af == lal->af) && | ||
1125 | (0 == memcmp (&lal->addr, | ||
1126 | &pos->addr, | ||
1127 | (AF_INET == lal->af) | ||
1128 | ? sizeof (struct sockaddr_in) | ||
1129 | : sizeof (struct sockaddr_in6))) ) | ||
1130 | found = GNUNET_YES; | ||
1131 | } | ||
1132 | if (GNUNET_NO == found) | ||
1133 | notify_clients (lal, | ||
1134 | GNUNET_NO); | ||
1135 | } | 1472 | } |
1136 | 1473 | while (NULL != (se = se_head)) | |
1137 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | ||
1138 | NULL != pos; | ||
1139 | pos = pos->next) | ||
1140 | { | 1474 | { |
1141 | found = GNUNET_NO; | 1475 | GNUNET_CONTAINER_DLL_remove (se_head, |
1142 | for (struct LocalAddressList *lal = lal_head; | 1476 | se_tail, |
1143 | NULL != lal; | 1477 | se); |
1144 | lal = lal->next) | 1478 | GNUNET_SCHEDULER_cancel (se->timeout_task); |
1145 | { | 1479 | GNUNET_free (se); |
1146 | if ( (pos->af == lal->af) && | 1480 | } |
1147 | (0 == memcmp (&lal->addr, | 1481 | if (NULL != scan_task) |
1148 | &pos->addr, | 1482 | { |
1149 | (AF_INET == lal->af) | 1483 | GNUNET_SCHEDULER_cancel (scan_task); |
1150 | ? sizeof (struct sockaddr_in) | 1484 | scan_task = NULL; |
1151 | : sizeof (struct sockaddr_in6))) ) | 1485 | } |
1152 | found = GNUNET_YES; | 1486 | if (NULL != stats) |
1153 | } | 1487 | { |
1154 | if (GNUNET_NO == found) | 1488 | GNUNET_STATISTICS_destroy (stats, |
1155 | notify_clients (pos, | 1489 | GNUNET_NO); |
1156 | GNUNET_YES); | 1490 | stats = NULL; |
1157 | } | 1491 | } |
1158 | |||
1159 | destroy_lal (); | 1492 | destroy_lal (); |
1160 | lal_head = ifc_ctx.lal_head; | ||
1161 | lal_tail = ifc_ctx.lal_tail; | ||
1162 | } | 1493 | } |
1163 | 1494 | ||
1164 | 1495 | ||
1165 | /** | 1496 | /** |
1166 | * Handle network size estimate clients. | 1497 | * Setup NAT service. |
1167 | * | 1498 | * |
1168 | * @param cls closure | 1499 | * @param cls closure |
1169 | * @param c configuration to use | 1500 | * @param c configuration to use |
@@ -1181,6 +1512,26 @@ run (void *cls, | |||
1181 | "STUN_STALE", | 1512 | "STUN_STALE", |
1182 | &stun_stale_timeout)) | 1513 | &stun_stale_timeout)) |
1183 | stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; | 1514 | stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; |
1515 | |||
1516 | /* Check for UPnP */ | ||
1517 | enable_upnp | ||
1518 | = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
1519 | "NAT", | ||
1520 | "ENABLE_UPNP"); | ||
1521 | if (GNUNET_YES == enable_upnp) | ||
1522 | { | ||
1523 | /* check if it works */ | ||
1524 | if (GNUNET_SYSERR == | ||
1525 | GNUNET_OS_check_helper_binary ("upnpc", | ||
1526 | GNUNET_NO, | ||
1527 | NULL)) | ||
1528 | { | ||
1529 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1530 | _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n")); | ||
1531 | enable_upnp = GNUNET_SYSERR; | ||
1532 | } | ||
1533 | } | ||
1534 | |||
1184 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | 1535 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, |
1185 | NULL); | 1536 | NULL); |
1186 | stats = GNUNET_STATISTICS_create ("nat", | 1537 | stats = GNUNET_STATISTICS_create ("nat", |
diff --git a/src/nat/gnunet-service-nat_mini.c b/src/nat/gnunet-service-nat_mini.c new file mode 100644 index 000000000..658ec72b7 --- /dev/null +++ b/src/nat/gnunet-service-nat_mini.c | |||
@@ -0,0 +1,759 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011-2014, 2016 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 | /** | ||
22 | * @file nat/gnunet-service-nat_mini.c | ||
23 | * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_nat_service.h" | ||
29 | #include "gnunet-service-nat_mini.h" | ||
30 | #include "nat.h" | ||
31 | |||
32 | #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__) | ||
33 | |||
34 | /** | ||
35 | * How long do we give upnpc to create a mapping? | ||
36 | */ | ||
37 | #define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) | ||
38 | |||
39 | /** | ||
40 | * How long do we give upnpc to remove a mapping? | ||
41 | */ | ||
42 | #define UNMAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) | ||
43 | |||
44 | /** | ||
45 | * How often do we check for changes in the mapping? | ||
46 | */ | ||
47 | #define MAP_REFRESH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) | ||
48 | |||
49 | |||
50 | /* ************************* external-ip calling ************************ */ | ||
51 | |||
52 | /** | ||
53 | * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation. | ||
54 | */ | ||
55 | struct GNUNET_NAT_ExternalHandle | ||
56 | { | ||
57 | |||
58 | /** | ||
59 | * Function to call with the result. | ||
60 | */ | ||
61 | GNUNET_NAT_IPCallback cb; | ||
62 | |||
63 | /** | ||
64 | * Closure for @e cb. | ||
65 | */ | ||
66 | void *cb_cls; | ||
67 | |||
68 | /** | ||
69 | * Read task. | ||
70 | */ | ||
71 | struct GNUNET_SCHEDULER_Task *task; | ||
72 | |||
73 | /** | ||
74 | * Handle to `external-ip` process. | ||
75 | */ | ||
76 | struct GNUNET_OS_Process *eip; | ||
77 | |||
78 | /** | ||
79 | * Handle to stdout pipe of `external-ip`. | ||
80 | */ | ||
81 | struct GNUNET_DISK_PipeHandle *opipe; | ||
82 | |||
83 | /** | ||
84 | * Read handle of @e opipe. | ||
85 | */ | ||
86 | const struct GNUNET_DISK_FileHandle *r; | ||
87 | |||
88 | /** | ||
89 | * Number of bytes in @e buf that are valid. | ||
90 | */ | ||
91 | size_t off; | ||
92 | |||
93 | /** | ||
94 | * Destination of our read operation (output of 'external-ip'). | ||
95 | */ | ||
96 | char buf[17]; | ||
97 | |||
98 | /** | ||
99 | * Error code for better debugging and user feedback | ||
100 | */ | ||
101 | enum GNUNET_NAT_StatusCode ret; | ||
102 | }; | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Read the output of `external-ip` into `buf`. When complete, parse | ||
107 | * the address and call our callback. | ||
108 | * | ||
109 | * @param cls the `struct GNUNET_NAT_ExternalHandle` | ||
110 | */ | ||
111 | static void | ||
112 | read_external_ipv4 (void *cls) | ||
113 | { | ||
114 | struct GNUNET_NAT_ExternalHandle *eh = cls; | ||
115 | ssize_t ret; | ||
116 | struct in_addr addr; | ||
117 | |||
118 | eh->task = NULL; | ||
119 | ret = GNUNET_DISK_file_read (eh->r, | ||
120 | &eh->buf[eh->off], | ||
121 | sizeof (eh->buf) - eh->off); | ||
122 | if (ret > 0) | ||
123 | { | ||
124 | /* try to read more */ | ||
125 | eh->off += ret; | ||
126 | eh->task | ||
127 | = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
128 | eh->r, | ||
129 | &read_external_ipv4, | ||
130 | eh); | ||
131 | return; | ||
132 | } | ||
133 | eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID; | ||
134 | if ( (eh->off > 7) && | ||
135 | (eh->buf[eh->off - 1] == '\n') ) | ||
136 | { | ||
137 | eh->buf[eh->off - 1] = '\0'; | ||
138 | if (1 == inet_pton (AF_INET, | ||
139 | eh->buf, | ||
140 | &addr)) | ||
141 | { | ||
142 | if (0 != addr.s_addr) | ||
143 | eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */ | ||
144 | else | ||
145 | eh->ret = GNUNET_NAT_ERROR_SUCCESS; | ||
146 | } | ||
147 | } | ||
148 | eh->cb (eh->cb_cls, | ||
149 | (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL, | ||
150 | eh->ret); | ||
151 | GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh); | ||
152 | } | ||
153 | |||
154 | |||
155 | /** | ||
156 | * (Asynchronously) signal error invoking `external-ip` to client. | ||
157 | * | ||
158 | * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed) | ||
159 | */ | ||
160 | static void | ||
161 | signal_external_ip_error (void *cls) | ||
162 | { | ||
163 | struct GNUNET_NAT_ExternalHandle *eh = cls; | ||
164 | |||
165 | eh->task = NULL; | ||
166 | eh->cb (eh->cb_cls, | ||
167 | NULL, | ||
168 | eh->ret); | ||
169 | GNUNET_free (eh); | ||
170 | } | ||
171 | |||
172 | |||
173 | /** | ||
174 | * Try to get the external IPv4 address of this peer. | ||
175 | * | ||
176 | * @param cb function to call with result | ||
177 | * @param cb_cls closure for @a cb | ||
178 | * @return handle for cancellation (can only be used until @a cb is called), never NULL | ||
179 | */ | ||
180 | struct GNUNET_NAT_ExternalHandle * | ||
181 | GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, | ||
182 | void *cb_cls) | ||
183 | { | ||
184 | struct GNUNET_NAT_ExternalHandle *eh; | ||
185 | |||
186 | eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle); | ||
187 | eh->cb = cb; | ||
188 | eh->cb_cls = cb_cls; | ||
189 | eh->ret = GNUNET_NAT_ERROR_SUCCESS; | ||
190 | if (GNUNET_SYSERR == | ||
191 | GNUNET_OS_check_helper_binary ("external-ip", | ||
192 | GNUNET_NO, | ||
193 | NULL)) | ||
194 | { | ||
195 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
196 | _("`external-ip' command not found\n")); | ||
197 | eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND; | ||
198 | eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, | ||
199 | eh); | ||
200 | return eh; | ||
201 | } | ||
202 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
203 | "Running `external-ip' to determine our external IP\n"); | ||
204 | eh->opipe = GNUNET_DISK_pipe (GNUNET_YES, | ||
205 | GNUNET_YES, | ||
206 | GNUNET_NO, | ||
207 | GNUNET_YES); | ||
208 | if (NULL == eh->opipe) | ||
209 | { | ||
210 | eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE; | ||
211 | eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, | ||
212 | eh); | ||
213 | return eh; | ||
214 | } | ||
215 | eh->eip = | ||
216 | GNUNET_OS_start_process (GNUNET_NO, | ||
217 | 0, | ||
218 | NULL, | ||
219 | eh->opipe, | ||
220 | NULL, | ||
221 | "external-ip", | ||
222 | "external-ip", | ||
223 | NULL); | ||
224 | if (NULL == eh->eip) | ||
225 | { | ||
226 | GNUNET_DISK_pipe_close (eh->opipe); | ||
227 | eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED; | ||
228 | eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, | ||
229 | eh); | ||
230 | return eh; | ||
231 | } | ||
232 | GNUNET_DISK_pipe_close_end (eh->opipe, | ||
233 | GNUNET_DISK_PIPE_END_WRITE); | ||
234 | eh->r = GNUNET_DISK_pipe_handle (eh->opipe, | ||
235 | GNUNET_DISK_PIPE_END_READ); | ||
236 | eh->task | ||
237 | = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
238 | eh->r, | ||
239 | &read_external_ipv4, | ||
240 | eh); | ||
241 | return eh; | ||
242 | } | ||
243 | |||
244 | |||
245 | /** | ||
246 | * Cancel operation. | ||
247 | * | ||
248 | * @param eh operation to cancel | ||
249 | */ | ||
250 | void | ||
251 | GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh) | ||
252 | { | ||
253 | if (NULL != eh->eip) | ||
254 | { | ||
255 | (void) GNUNET_OS_process_kill (eh->eip, | ||
256 | SIGKILL); | ||
257 | GNUNET_OS_process_destroy (eh->eip); | ||
258 | } | ||
259 | if (NULL != eh->opipe) | ||
260 | { | ||
261 | GNUNET_DISK_pipe_close (eh->opipe); | ||
262 | eh->opipe = NULL; | ||
263 | } | ||
264 | if (NULL != eh->task) | ||
265 | { | ||
266 | GNUNET_SCHEDULER_cancel (eh->task); | ||
267 | eh->task = NULL; | ||
268 | } | ||
269 | GNUNET_free (eh); | ||
270 | } | ||
271 | |||
272 | |||
273 | /* ************************* upnpc calling ************************ */ | ||
274 | |||
275 | |||
276 | /** | ||
277 | * Handle to a mapping created with upnpc. | ||
278 | */ | ||
279 | struct GNUNET_NAT_MiniHandle | ||
280 | { | ||
281 | |||
282 | /** | ||
283 | * Function to call on mapping changes. | ||
284 | */ | ||
285 | GNUNET_NAT_MiniAddressCallback ac; | ||
286 | |||
287 | /** | ||
288 | * Closure for @e ac. | ||
289 | */ | ||
290 | void *ac_cls; | ||
291 | |||
292 | /** | ||
293 | * Command used to install the map. | ||
294 | */ | ||
295 | struct GNUNET_OS_CommandHandle *map_cmd; | ||
296 | |||
297 | /** | ||
298 | * Command used to refresh our map information. | ||
299 | */ | ||
300 | struct GNUNET_OS_CommandHandle *refresh_cmd; | ||
301 | |||
302 | /** | ||
303 | * Command used to remove the mapping. | ||
304 | */ | ||
305 | struct GNUNET_OS_CommandHandle *unmap_cmd; | ||
306 | |||
307 | /** | ||
308 | * Our current external mapping (if we have one). | ||
309 | */ | ||
310 | struct sockaddr_in current_addr; | ||
311 | |||
312 | /** | ||
313 | * We check the mapping periodically to see if it | ||
314 | * still works. This task triggers the check. | ||
315 | */ | ||
316 | struct GNUNET_SCHEDULER_Task *refresh_task; | ||
317 | |||
318 | /** | ||
319 | * Are we mapping TCP or UDP? | ||
320 | */ | ||
321 | int is_tcp; | ||
322 | |||
323 | /** | ||
324 | * Did we succeed with creating a mapping? | ||
325 | */ | ||
326 | int did_map; | ||
327 | |||
328 | /** | ||
329 | * Did we find our mapping during refresh scan? | ||
330 | */ | ||
331 | int found; | ||
332 | |||
333 | /** | ||
334 | * Which port are we mapping? | ||
335 | */ | ||
336 | uint16_t port; | ||
337 | |||
338 | }; | ||
339 | |||
340 | |||
341 | /** | ||
342 | * Run "upnpc -l" to find out if our mapping changed. | ||
343 | * | ||
344 | * @param cls the `struct GNUNET_NAT_MiniHandle` | ||
345 | */ | ||
346 | static void | ||
347 | do_refresh (void *cls); | ||
348 | |||
349 | |||
350 | /** | ||
351 | * Process the output from the "upnpc -r" command. | ||
352 | * | ||
353 | * @param cls the `struct GNUNET_NAT_MiniHandle` | ||
354 | * @param line line of output, NULL at the end | ||
355 | */ | ||
356 | static void | ||
357 | process_map_output (void *cls, | ||
358 | const char *line); | ||
359 | |||
360 | |||
361 | /** | ||
362 | * Run "upnpc -r" to map our internal port. | ||
363 | * | ||
364 | * @param mini our handle | ||
365 | */ | ||
366 | static void | ||
367 | run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini) | ||
368 | { | ||
369 | char pstr[6]; | ||
370 | |||
371 | GNUNET_snprintf (pstr, | ||
372 | sizeof (pstr), | ||
373 | "%u", | ||
374 | (unsigned int) mini->port); | ||
375 | mini->map_cmd | ||
376 | = GNUNET_OS_command_run (&process_map_output, | ||
377 | mini, | ||
378 | MAP_TIMEOUT, | ||
379 | "upnpc", | ||
380 | "upnpc", | ||
381 | "-r", | ||
382 | pstr, | ||
383 | mini->is_tcp ? "tcp" : "udp", | ||
384 | NULL); | ||
385 | if (NULL == mini->map_cmd) | ||
386 | { | ||
387 | mini->ac (mini->ac_cls, | ||
388 | GNUNET_SYSERR, | ||
389 | NULL, | ||
390 | 0, | ||
391 | GNUNET_NAT_ERROR_UPNPC_FAILED); | ||
392 | return; | ||
393 | } | ||
394 | } | ||
395 | |||
396 | |||
397 | /** | ||
398 | * Process the output from "upnpc -l" to see if our | ||
399 | * external mapping changed. If so, do the notifications. | ||
400 | * | ||
401 | * @param cls the `struct GNUNET_NAT_MiniHandle` | ||
402 | * @param line line of output, NULL at the end | ||
403 | */ | ||
404 | static void | ||
405 | process_refresh_output (void *cls, | ||
406 | const char *line) | ||
407 | { | ||
408 | struct GNUNET_NAT_MiniHandle *mini = cls; | ||
409 | char pstr[9]; | ||
410 | const char *s; | ||
411 | unsigned int nport; | ||
412 | struct in_addr exip; | ||
413 | |||
414 | if (NULL == line) | ||
415 | { | ||
416 | GNUNET_OS_command_stop (mini->refresh_cmd); | ||
417 | mini->refresh_cmd = NULL; | ||
418 | if (GNUNET_NO == mini->found) | ||
419 | { | ||
420 | /* mapping disappeared, try to re-create */ | ||
421 | if (GNUNET_YES == mini->did_map) | ||
422 | { | ||
423 | mini->ac (mini->ac_cls, | ||
424 | GNUNET_NO, | ||
425 | (const struct sockaddr *) &mini->current_addr, | ||
426 | sizeof (mini->current_addr), | ||
427 | GNUNET_NAT_ERROR_SUCCESS); | ||
428 | mini->did_map = GNUNET_NO; | ||
429 | } | ||
430 | run_upnpc_r (mini); | ||
431 | } | ||
432 | return; | ||
433 | } | ||
434 | if (!mini->did_map) | ||
435 | return; /* never mapped, won't find our mapping anyway */ | ||
436 | |||
437 | /* we're looking for output of the form: | ||
438 | * "ExternalIPAddress = 12.134.41.124" */ | ||
439 | |||
440 | s = strstr (line, | ||
441 | "ExternalIPAddress = "); | ||
442 | if (NULL != s) | ||
443 | { | ||
444 | s += strlen ("ExternalIPAddress = "); | ||
445 | if (1 != inet_pton (AF_INET, | ||
446 | s, | ||
447 | &exip)) | ||
448 | return; /* skip */ | ||
449 | if (exip.s_addr == mini->current_addr.sin_addr.s_addr) | ||
450 | return; /* no change */ | ||
451 | /* update mapping */ | ||
452 | mini->ac (mini->ac_cls, | ||
453 | GNUNET_NO, | ||
454 | (const struct sockaddr *) &mini->current_addr, | ||
455 | sizeof (mini->current_addr), | ||
456 | GNUNET_NAT_ERROR_SUCCESS); | ||
457 | mini->current_addr.sin_addr = exip; | ||
458 | mini->ac (mini->ac_cls, | ||
459 | GNUNET_YES, | ||
460 | (const struct sockaddr *) &mini->current_addr, | ||
461 | sizeof (mini->current_addr), | ||
462 | GNUNET_NAT_ERROR_SUCCESS); | ||
463 | return; | ||
464 | } | ||
465 | /* | ||
466 | * we're looking for output of the form: | ||
467 | * | ||
468 | * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''" | ||
469 | * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''" | ||
470 | * | ||
471 | * the pattern we look for is: | ||
472 | * | ||
473 | * "%s TCP PORT->STRING:OURPORT *" or | ||
474 | * "%s UDP PORT->STRING:OURPORT *" | ||
475 | */ | ||
476 | GNUNET_snprintf (pstr, | ||
477 | sizeof (pstr), | ||
478 | ":%u ", | ||
479 | mini->port); | ||
480 | if (NULL == (s = strstr (line, "->"))) | ||
481 | return; /* skip */ | ||
482 | if (NULL == strstr (s, pstr)) | ||
483 | return; /* skip */ | ||
484 | if (1 != | ||
485 | SSCANF (line, | ||
486 | (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" : | ||
487 | "%*u UDP %u->%*s:%*u %*s", &nport)) | ||
488 | return; /* skip */ | ||
489 | mini->found = GNUNET_YES; | ||
490 | if (nport == ntohs (mini->current_addr.sin_port)) | ||
491 | return; /* no change */ | ||
492 | |||
493 | /* external port changed, update mapping */ | ||
494 | mini->ac (mini->ac_cls, | ||
495 | GNUNET_NO, | ||
496 | (const struct sockaddr *) &mini->current_addr, | ||
497 | sizeof (mini->current_addr), | ||
498 | GNUNET_NAT_ERROR_SUCCESS); | ||
499 | mini->current_addr.sin_port = htons ((uint16_t) nport); | ||
500 | mini->ac (mini->ac_cls, | ||
501 | GNUNET_YES, | ||
502 | (const struct sockaddr *) &mini->current_addr, | ||
503 | sizeof (mini->current_addr), | ||
504 | GNUNET_NAT_ERROR_SUCCESS); | ||
505 | } | ||
506 | |||
507 | |||
508 | /** | ||
509 | * Run "upnpc -l" to find out if our mapping changed. | ||
510 | * | ||
511 | * @param cls the 'struct GNUNET_NAT_MiniHandle' | ||
512 | */ | ||
513 | static void | ||
514 | do_refresh (void *cls) | ||
515 | { | ||
516 | struct GNUNET_NAT_MiniHandle *mini = cls; | ||
517 | int ac; | ||
518 | |||
519 | mini->refresh_task | ||
520 | = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, | ||
521 | &do_refresh, | ||
522 | mini); | ||
523 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
524 | "Running `upnpc' to check if our mapping still exists\n"); | ||
525 | mini->found = GNUNET_NO; | ||
526 | ac = GNUNET_NO; | ||
527 | if (NULL != mini->map_cmd) | ||
528 | { | ||
529 | /* took way too long, abort it! */ | ||
530 | GNUNET_OS_command_stop (mini->map_cmd); | ||
531 | mini->map_cmd = NULL; | ||
532 | ac = GNUNET_YES; | ||
533 | } | ||
534 | if (NULL != mini->refresh_cmd) | ||
535 | { | ||
536 | /* took way too long, abort it! */ | ||
537 | GNUNET_OS_command_stop (mini->refresh_cmd); | ||
538 | mini->refresh_cmd = NULL; | ||
539 | ac = GNUNET_YES; | ||
540 | } | ||
541 | mini->refresh_cmd = | ||
542 | GNUNET_OS_command_run (&process_refresh_output, | ||
543 | mini, | ||
544 | MAP_TIMEOUT, | ||
545 | "upnpc", | ||
546 | "upnpc", | ||
547 | "-l", | ||
548 | NULL); | ||
549 | if (GNUNET_YES == ac) | ||
550 | mini->ac (mini->ac_cls, | ||
551 | GNUNET_SYSERR, | ||
552 | NULL, | ||
553 | 0, | ||
554 | GNUNET_NAT_ERROR_UPNPC_TIMEOUT); | ||
555 | } | ||
556 | |||
557 | |||
558 | /** | ||
559 | * Process the output from the 'upnpc -r' command. | ||
560 | * | ||
561 | * @param cls the `struct GNUNET_NAT_MiniHandle` | ||
562 | * @param line line of output, NULL at the end | ||
563 | */ | ||
564 | static void | ||
565 | process_map_output (void *cls, | ||
566 | const char *line) | ||
567 | { | ||
568 | struct GNUNET_NAT_MiniHandle *mini = cls; | ||
569 | const char *ipaddr; | ||
570 | char *ipa; | ||
571 | const char *pstr; | ||
572 | unsigned int port; | ||
573 | |||
574 | if (NULL == line) | ||
575 | { | ||
576 | GNUNET_OS_command_stop (mini->map_cmd); | ||
577 | mini->map_cmd = NULL; | ||
578 | if (GNUNET_YES != mini->did_map) | ||
579 | mini->ac (mini->ac_cls, | ||
580 | GNUNET_SYSERR, | ||
581 | NULL, 0, | ||
582 | GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED); | ||
583 | if (NULL == mini->refresh_task) | ||
584 | mini->refresh_task = | ||
585 | GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, | ||
586 | &do_refresh, | ||
587 | mini); | ||
588 | return; | ||
589 | } | ||
590 | /* | ||
591 | * The upnpc output we're after looks like this: | ||
592 | * | ||
593 | * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000" | ||
594 | */ | ||
595 | if ((NULL == (ipaddr = strstr (line, " "))) || | ||
596 | (NULL == (pstr = strstr (ipaddr, ":"))) || | ||
597 | (1 != SSCANF (pstr + 1, "%u", &port))) | ||
598 | { | ||
599 | return; /* skip line */ | ||
600 | } | ||
601 | ipa = GNUNET_strdup (ipaddr + 1); | ||
602 | strstr (ipa, ":")[0] = '\0'; | ||
603 | if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr)) | ||
604 | { | ||
605 | GNUNET_free (ipa); | ||
606 | return; /* skip line */ | ||
607 | } | ||
608 | GNUNET_free (ipa); | ||
609 | |||
610 | mini->current_addr.sin_port = htons (port); | ||
611 | mini->current_addr.sin_family = AF_INET; | ||
612 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
613 | mini->current_addr.sin_len = sizeof (struct sockaddr_in); | ||
614 | #endif | ||
615 | mini->did_map = GNUNET_YES; | ||
616 | mini->ac (mini->ac_cls, GNUNET_YES, | ||
617 | (const struct sockaddr *) &mini->current_addr, | ||
618 | sizeof (mini->current_addr), | ||
619 | GNUNET_NAT_ERROR_SUCCESS); | ||
620 | } | ||
621 | |||
622 | |||
623 | /** | ||
624 | * Start mapping the given port using (mini)upnpc. This function | ||
625 | * should typically not be used directly (it is used within the | ||
626 | * general-purpose #GNUNET_NAT_register() code). However, it can be | ||
627 | * used if specifically UPnP-based NAT traversal is to be used or | ||
628 | * tested. | ||
629 | * | ||
630 | * @param port port to map | ||
631 | * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP | ||
632 | * @param ac function to call with mapping result | ||
633 | * @param ac_cls closure for @a ac | ||
634 | * @return NULL on error (no 'upnpc' installed) | ||
635 | */ | ||
636 | struct GNUNET_NAT_MiniHandle * | ||
637 | GNUNET_NAT_mini_map_start (uint16_t port, | ||
638 | int is_tcp, | ||
639 | GNUNET_NAT_MiniAddressCallback ac, | ||
640 | void *ac_cls) | ||
641 | { | ||
642 | struct GNUNET_NAT_MiniHandle *ret; | ||
643 | |||
644 | if (GNUNET_SYSERR == | ||
645 | GNUNET_OS_check_helper_binary ("upnpc", | ||
646 | GNUNET_NO, | ||
647 | NULL)) | ||
648 | { | ||
649 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
650 | _("`upnpc' command not found\n")); | ||
651 | ac (ac_cls, | ||
652 | GNUNET_SYSERR, | ||
653 | NULL, 0, | ||
654 | GNUNET_NAT_ERROR_UPNPC_NOT_FOUND); | ||
655 | return NULL; | ||
656 | } | ||
657 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
658 | "Running `upnpc' to install mapping\n"); | ||
659 | ret = GNUNET_new (struct GNUNET_NAT_MiniHandle); | ||
660 | ret->ac = ac; | ||
661 | ret->ac_cls = ac_cls; | ||
662 | ret->is_tcp = is_tcp; | ||
663 | ret->port = port; | ||
664 | ret->refresh_task = | ||
665 | GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, | ||
666 | &do_refresh, | ||
667 | ret); | ||
668 | run_upnpc_r (ret); | ||
669 | return ret; | ||
670 | } | ||
671 | |||
672 | |||
673 | /** | ||
674 | * Process output from our 'unmap' command. | ||
675 | * | ||
676 | * @param cls the `struct GNUNET_NAT_MiniHandle` | ||
677 | * @param line line of output, NULL at the end | ||
678 | */ | ||
679 | static void | ||
680 | process_unmap_output (void *cls, | ||
681 | const char *line) | ||
682 | { | ||
683 | struct GNUNET_NAT_MiniHandle *mini = cls; | ||
684 | |||
685 | if (NULL == line) | ||
686 | { | ||
687 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
688 | "UPnP unmap done\n"); | ||
689 | GNUNET_OS_command_stop (mini->unmap_cmd); | ||
690 | mini->unmap_cmd = NULL; | ||
691 | GNUNET_free (mini); | ||
692 | return; | ||
693 | } | ||
694 | /* we don't really care about the output... */ | ||
695 | } | ||
696 | |||
697 | |||
698 | /** | ||
699 | * Remove a mapping created with (mini)upnpc. Calling | ||
700 | * this function will give 'upnpc' 1s to remove tha mapping, | ||
701 | * so while this function is non-blocking, a task will be | ||
702 | * left with the scheduler for up to 1s past this call. | ||
703 | * | ||
704 | * @param mini the handle | ||
705 | */ | ||
706 | void | ||
707 | GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini) | ||
708 | { | ||
709 | char pstr[6]; | ||
710 | |||
711 | if (NULL != mini->refresh_task) | ||
712 | { | ||
713 | GNUNET_SCHEDULER_cancel (mini->refresh_task); | ||
714 | mini->refresh_task = NULL; | ||
715 | } | ||
716 | if (NULL != mini->refresh_cmd) | ||
717 | { | ||
718 | GNUNET_OS_command_stop (mini->refresh_cmd); | ||
719 | mini->refresh_cmd = NULL; | ||
720 | } | ||
721 | if (NULL != mini->map_cmd) | ||
722 | { | ||
723 | GNUNET_OS_command_stop (mini->map_cmd); | ||
724 | mini->map_cmd = NULL; | ||
725 | } | ||
726 | if (GNUNET_NO == mini->did_map) | ||
727 | { | ||
728 | GNUNET_free (mini); | ||
729 | return; | ||
730 | } | ||
731 | mini->ac (mini->ac_cls, | ||
732 | GNUNET_NO, | ||
733 | (const struct sockaddr *) &mini->current_addr, | ||
734 | sizeof (mini->current_addr), | ||
735 | GNUNET_NAT_ERROR_SUCCESS); | ||
736 | /* Note: oddly enough, deletion uses the external port whereas | ||
737 | * addition uses the internal port; this rarely matters since they | ||
738 | * often are the same, but it might... */ | ||
739 | GNUNET_snprintf (pstr, | ||
740 | sizeof (pstr), | ||
741 | "%u", | ||
742 | (unsigned int) ntohs (mini->current_addr.sin_port)); | ||
743 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
744 | "Unmapping port %u with UPnP\n", | ||
745 | ntohs (mini->current_addr.sin_port)); | ||
746 | mini->unmap_cmd | ||
747 | = GNUNET_OS_command_run (&process_unmap_output, | ||
748 | mini, | ||
749 | UNMAP_TIMEOUT, | ||
750 | "upnpc", | ||
751 | "upnpc", | ||
752 | "-d", | ||
753 | pstr, | ||
754 | mini->is_tcp ? "tcp" : "udp", | ||
755 | NULL); | ||
756 | } | ||
757 | |||
758 | |||
759 | /* end of gnunet-service-nat_mini.c */ | ||
diff --git a/src/nat/gnunet-service-nat_mini.h b/src/nat/gnunet-service-nat_mini.h new file mode 100644 index 000000000..2c0dd3445 --- /dev/null +++ b/src/nat/gnunet-service-nat_mini.h | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011-2014, 2016 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 | /** | ||
22 | * @file nat/gnunet-service-nat_mini.c | ||
23 | * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #ifndef GNUNET_SERVICE_NAT_MINI_H | ||
27 | #define GNUNET_SERVICE_NAT_MINI_H | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Signature of a callback that is given an IP address. | ||
32 | * | ||
33 | * @param cls closure | ||
34 | * @param addr the address, NULL on errors | ||
35 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code | ||
36 | */ | ||
37 | typedef void | ||
38 | (*GNUNET_NAT_IPCallback) (void *cls, | ||
39 | const struct in_addr *addr, | ||
40 | enum GNUNET_NAT_StatusCode result); | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation. | ||
45 | */ | ||
46 | struct GNUNET_NAT_ExternalHandle; | ||
47 | |||
48 | |||
49 | /** | ||
50 | * Try to get the external IPv4 address of this peer. | ||
51 | * | ||
52 | * @param cb function to call with result | ||
53 | * @param cb_cls closure for @a cb | ||
54 | * @return handle for cancellation (can only be used until @a cb is called), NULL on error | ||
55 | */ | ||
56 | struct GNUNET_NAT_ExternalHandle * | ||
57 | GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, | ||
58 | void *cb_cls); | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Cancel operation. | ||
63 | * | ||
64 | * @param eh operation to cancel | ||
65 | */ | ||
66 | void | ||
67 | GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh); | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Handle to a mapping created with upnpc. | ||
72 | */ | ||
73 | struct GNUNET_NAT_MiniHandle; | ||
74 | |||
75 | |||
76 | /** | ||
77 | * Signature of the callback passed to #GNUNET_NAT_register() for | ||
78 | * a function to call whenever our set of 'valid' addresses changes. | ||
79 | * | ||
80 | * @param cls closure | ||
81 | * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean | ||
82 | * the previous (now invalid) one, #GNUNET_SYSERR indicates an error | ||
83 | * @param addr either the previous or the new public IP address | ||
84 | * @param addrlen actual length of the @a addr | ||
85 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code | ||
86 | */ | ||
87 | typedef void | ||
88 | (*GNUNET_NAT_MiniAddressCallback) (void *cls, | ||
89 | int add_remove, | ||
90 | const struct sockaddr *addr, | ||
91 | socklen_t addrlen, | ||
92 | enum GNUNET_NAT_StatusCode result); | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Start mapping the given port using (mini)upnpc. This function | ||
97 | * should typically not be used directly (it is used within the | ||
98 | * general-purpose #GNUNET_NAT_register() code). However, it can be | ||
99 | * used if specifically UPnP-based NAT traversal is to be used or | ||
100 | * tested. | ||
101 | * | ||
102 | * @param port port to map | ||
103 | * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP | ||
104 | * @param ac function to call with mapping result | ||
105 | * @param ac_cls closure for @a ac | ||
106 | * @return NULL on error | ||
107 | */ | ||
108 | struct GNUNET_NAT_MiniHandle * | ||
109 | GNUNET_NAT_mini_map_start (uint16_t port, | ||
110 | int is_tcp, | ||
111 | GNUNET_NAT_MiniAddressCallback ac, | ||
112 | void *ac_cls); | ||
113 | |||
114 | |||
115 | /** | ||
116 | * Remove a mapping created with (mini)upnpc. Calling | ||
117 | * this function will give 'upnpc' 1s to remove the mapping, | ||
118 | * so while this function is non-blocking, a task will be | ||
119 | * left with the scheduler for up to 1s past this call. | ||
120 | * | ||
121 | * @param mini the handle | ||
122 | */ | ||
123 | void | ||
124 | GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini); | ||
125 | |||
126 | |||
127 | #endif | ||
diff --git a/src/nat/nat.h b/src/nat/nat.h index 6df72c0ab..3356b19ce 100644 --- a/src/nat/nat.h +++ b/src/nat/nat.h | |||
@@ -78,7 +78,7 @@ enum GNUNET_NAT_RegisterFlags | |||
78 | 78 | ||
79 | /** | 79 | /** |
80 | * This client wants to be informed about changes to our | 80 | * This client wants to be informed about changes to our |
81 | * external addresses. | 81 | * applicable addresses. |
82 | */ | 82 | */ |
83 | GNUNET_NAT_RF_ADDRESSES = 1, | 83 | GNUNET_NAT_RF_ADDRESSES = 1, |
84 | 84 | ||
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c index 58ed3e675..3fe97ed85 100644 --- a/src/nat/nat_api.c +++ b/src/nat/nat_api.c | |||
@@ -331,6 +331,7 @@ do_connect (void *cls) | |||
331 | nh), | 331 | nh), |
332 | GNUNET_MQ_handler_end () | 332 | GNUNET_MQ_handler_end () |
333 | }; | 333 | }; |
334 | struct GNUNET_MQ_Envelope *env; | ||
334 | 335 | ||
335 | nh->reconnect_task = NULL; | 336 | nh->reconnect_task = NULL; |
336 | nh->mq = GNUNET_CLIENT_connecT (nh->cfg, | 337 | nh->mq = GNUNET_CLIENT_connecT (nh->cfg, |
@@ -339,7 +340,13 @@ do_connect (void *cls) | |||
339 | &mq_error_handler, | 340 | &mq_error_handler, |
340 | nh); | 341 | nh); |
341 | if (NULL == nh->mq) | 342 | if (NULL == nh->mq) |
343 | { | ||
342 | reconnect (nh); | 344 | reconnect (nh); |
345 | return; | ||
346 | } | ||
347 | env = GNUNET_MQ_msg_copy (nh->reg); | ||
348 | GNUNET_MQ_send (nh->mq, | ||
349 | env); | ||
343 | } | 350 | } |
344 | 351 | ||
345 | 352 | ||
diff --git a/src/util/util.conf b/src/util/util.conf index 6b9c52d00..ecc94ead0 100644 --- a/src/util/util.conf +++ b/src/util/util.conf | |||
@@ -48,8 +48,18 @@ GNUNET_USER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/gnunet-${USERHOME:-${USER:-use | |||
48 | 48 | ||
49 | 49 | ||
50 | [PEER] | 50 | [PEER] |
51 | # Where do we store our private key? | ||
51 | PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc | 52 | PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc |
52 | 53 | ||
54 | # What kind of system are we on? Choices are | ||
55 | # INFRASTRUCTURE (always-on, grid, data center) | ||
56 | # DESKTOP (sometimes-on, grid, office) | ||
57 | # NOTEBOOK (sometimes-on, mobile, often limited network, | ||
58 | # if on-battery than large battery) | ||
59 | # MOBILE (sometimes-on, mobile, always limited network, | ||
60 | # always battery limited) | ||
61 | # UNKNOWN (not configured/specified/known) | ||
62 | SYSTEM_TYPE = UNKNOWN | ||
53 | 63 | ||
54 | [TESTING] | 64 | [TESTING] |
55 | SPEEDUP_INTERVAL = 0 ms | 65 | SPEEDUP_INTERVAL = 0 ms |