aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSchanzenbach, Martin <mschanzenbach@posteo.de>2016-12-27 16:43:48 +0100
committerSchanzenbach, Martin <mschanzenbach@posteo.de>2016-12-27 16:43:48 +0100
commit43b34377e10d329075327104e4a295ee9d3c53b4 (patch)
tree29819274064fba2cfa159ce94fe1862d0ae82969
parentb0937948acc28e39ac2ed53799dbc63bad8b2936 (diff)
parent9ba4c1d15e6bcb20c47dff1067ab76e86d7c0f8a (diff)
downloadgnunet-43b34377e10d329075327104e4a295ee9d3c53b4.tar.gz
gnunet-43b34377e10d329075327104e4a295ee9d3c53b4.zip
Merge remote-tracking branch 'origin/master' into credentials
-rw-r--r--doc/man/gnunet-nat.152
-rw-r--r--src/include/gnunet_configuration_lib.h2
-rw-r--r--src/include/gnunet_nat_service.h10
-rw-r--r--src/nat/Makefile.am1
-rw-r--r--src/nat/gnunet-nat-server.c44
-rw-r--r--src/nat/gnunet-nat.c108
-rw-r--r--src/nat/gnunet-service-nat.c1392
-rw-r--r--src/nat/gnunet-service-nat_mini.c763
-rw-r--r--src/nat/gnunet-service-nat_mini.h127
-rw-r--r--src/nat/nat.h2
-rw-r--r--src/nat/nat_api.c27
-rw-r--r--src/nat/nat_api_stun.c4
-rw-r--r--src/nat/nat_stun.h21
-rw-r--r--src/util/util.conf10
14 files changed, 2219 insertions, 344 deletions
diff --git a/doc/man/gnunet-nat.1 b/doc/man/gnunet-nat.1
index a43223fa8..2ba236399 100644
--- a/doc/man/gnunet-nat.1
+++ b/doc/man/gnunet-nat.1
@@ -39,10 +39,6 @@ Assuming we are listening at ADDRESS for connection reversal requests.
39Ask the peer at ADDRESS for connection reversal, using the local address for the target address of the reversal. 39Ask the peer at ADDRESS for connection reversal, using the local address for the target address of the reversal.
40 40
41.B 41.B
42.IP "\-L, \-\-listen"
43Listen for connection reversal requests.
44
45.B
46.IP "\-p PORT, \-\-port=PORT" 42.IP "\-p PORT, \-\-port=PORT"
47Use PORT as our external port for advertising for incoming requests. 43Use PORT as our external port for advertising for incoming requests.
48 44
@@ -58,6 +54,54 @@ Use TCP.
58.IP "\-u, \-\-udp" 54.IP "\-u, \-\-udp"
59Use UDP. 55Use UDP.
60 56
57.B
58.IP "\-w, \-\-write"
59Write configuration to configuration file, useful in combination with autoconfiguration (\-a).
60
61.B
62.IP "\-W, \-\-watch"
63Watch for connection reversal requests.
64
65.SH EXAMPLES
66.PP
67
68\fBBasic examples\fR
69
70We are bound to "0.0.0.0:8080" on UDP and want to obtain all applicable IP addresses:
71
72 # gnunet-nat -i 0.0.0.0:8080 -u
73
74We are bound to "::0" on port 8080 on TCP and want to obtain all applicable IP addresses:
75
76 # gnunet-nat -i '[::0]':8080 -t
77
78We are bound to "127.0.0.1:8080" on UDP and want to obtain all applicable IP addresses:
79
80 # gnunet-nat -i 127.0.0.1:8080 -u
81
82\fBICMP-based NAT traversal:\fR
83
84Watch for connection reversal request:
85
86 # gnunet-nat FIXME
87
88Initiate connection reversal request:
89
90 # gnunet-nat FIXME
91
92\fBSTUN-based XXX:\fR
93
94XXX:
95
96 # gnunet-nat FIXME -s
97
98\fBAutomatic configuration:\fR
99
100Probe and write result to configuration:
101
102 # gnunet-nat -aw
103
104
61.SH BUGS 105.SH BUGS
62Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <gnunet\-developers@gnu.org> 106Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending electronic mail to <gnunet\-developers@gnu.org>
63 107
diff --git a/src/include/gnunet_configuration_lib.h b/src/include/gnunet_configuration_lib.h
index 945f3ca59..746dea61f 100644
--- a/src/include/gnunet_configuration_lib.h
+++ b/src/include/gnunet_configuration_lib.h
@@ -89,7 +89,7 @@ GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
89 89
90/** 90/**
91 * Load default configuration. This function will parse the 91 * Load default configuration. This function will parse the
92 * defaults from the given defaults_d directory. 92 * defaults from the given @a defaults_d directory.
93 * 93 *
94 * @param cfg configuration to update 94 * @param cfg configuration to update
95 * @param defaults_d directory with the defaults 95 * @param defaults_d directory with the defaults
diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h
index b66b44240..4df17b531 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,
@@ -114,11 +114,13 @@ enum GNUNET_NAT_AddressClass
114 GNUNET_NAT_AC_LOOPBACK = 64, 114 GNUNET_NAT_AC_LOOPBACK = 64,
115 115
116 /** 116 /**
117 * Addresses that should be our global external IP address 117 * Addresses that should be our external IP address
118 * on the outside of a NAT. Might be incorrectly determined. 118 * on the outside of a NAT. Might be incorrectly determined.
119 * Used as a bit in combination with #GNUNET_NAT_AC_GLOBAL. 119 * Used as a bit in combination with #GNUNET_NAT_AC_GLOBAL,
120 * or in case of double-NAT with
121 * #GNUNET_NAT_AC_LAN.
120 */ 122 */
121 GNUNET_NAT_AC_GLOBAL_EXTERN = 128, 123 GNUNET_NAT_AC_EXTERN = 128,
122 124
123 /** 125 /**
124 * Bitmask for "any" address. 126 * Bitmask for "any" address.
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 = \
98gnunet_service_nat_SOURCES = \ 98gnunet_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
102gnunet_service_nat_LDADD = \ 103gnunet_service_nat_LDADD = \
103 $(top_builddir)/src/util/libgnunetutil.la \ 104 $(top_builddir)/src/util/libgnunetutil.la \
diff --git a/src/nat/gnunet-nat-server.c b/src/nat/gnunet-nat-server.c
index 6722deefb..1692a8ef1 100644
--- a/src/nat/gnunet-nat-server.c
+++ b/src/nat/gnunet-nat-server.c
@@ -42,8 +42,7 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
42 42
43 43
44/** 44/**
45 * Try contacting the peer using autonomous 45 * Try contacting the peer using autonomous NAT traveral method.
46 * NAT traveral method.
47 * 46 *
48 * @param dst_ipv4 IPv4 address to send the fake ICMP message 47 * @param dst_ipv4 IPv4 address to send the fake ICMP message
49 * @param dport destination port to include in ICMP message 48 * @param dport destination port to include in ICMP message
@@ -78,7 +77,7 @@ try_anat (uint32_t dst_ipv4,
78 77
79 78
80/** 79/**
81 * Closure for 'tcp_send'. 80 * Closure for #tcp_send.
82 */ 81 */
83struct TcpContext 82struct TcpContext
84{ 83{
@@ -98,7 +97,7 @@ struct TcpContext
98 * Task called by the scheduler once we can do the TCP send 97 * Task called by the scheduler once we can do the TCP send
99 * (or once we failed to connect...). 98 * (or once we failed to connect...).
100 * 99 *
101 * @param cls the 'struct TcpContext' 100 * @param cls the `struct TcpContext`
102 */ 101 */
103static void 102static void
104tcp_send (void *cls) 103tcp_send (void *cls)
@@ -182,7 +181,7 @@ try_send_tcp (uint32_t dst_ipv4,
182 181
183/** 182/**
184 * Try to send @a data to the 183 * Try to send @a data to the
185 * IP @a dst_ipv4' at port @a dport via UDP. 184 * IP @a dst_ipv4 at port @a dport via UDP.
186 * 185 *
187 * @param dst_ipv4 target IP 186 * @param dst_ipv4 target IP
188 * @param dport target port 187 * @param dport target port
@@ -313,12 +312,13 @@ run (void *cls,
313 }; 312 };
314 313
315 cfg = c; 314 cfg = c;
316 if ((args[0] == NULL) || (1 != SSCANF (args[0], "%u", &port)) || (0 == port) 315 if ( (NULL == args[0]) ||
317 || (65536 <= port)) 316 (1 != SSCANF (args[0], "%u", &port)) ||
317 (0 == port) ||
318 (65536 <= port) )
318 { 319 {
319 FPRINTF (stderr, 320 FPRINTF (stderr,
320 _ 321 _("Please pass valid port number as the first argument! (got `%s')\n"),
321 ("Please pass valid port number as the first argument! (got `%s')\n"),
322 args[0]); 322 args[0]);
323 return; 323 return;
324 } 324 }
@@ -332,10 +332,14 @@ run (void *cls,
332 in4.sin_len = sizeof (in4); 332 in4.sin_len = sizeof (in4);
333 in6.sin6_len = sizeof (in6); 333 in6.sin6_len = sizeof (in6);
334#endif 334#endif
335 server = 335 server = GNUNET_SERVER_create (NULL,
336 GNUNET_SERVER_create (NULL, NULL, (struct sockaddr * const *) sa, slen, 336 NULL,
337 GNUNET_TIME_UNIT_SECONDS, GNUNET_YES); 337 (struct sockaddr * const *) sa,
338 GNUNET_SERVER_add_handlers (server, handlers); 338 slen,
339 GNUNET_TIME_UNIT_SECONDS,
340 GNUNET_YES);
341 GNUNET_SERVER_add_handlers (server,
342 handlers);
339 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 343 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
340 NULL); 344 NULL);
341} 345}
@@ -355,13 +359,19 @@ main (int argc, char *const argv[])
355 GNUNET_GETOPT_OPTION_END 359 GNUNET_GETOPT_OPTION_END
356 }; 360 };
357 361
358 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) 362 if (GNUNET_OK !=
363 GNUNET_STRINGS_get_utf8_args (argc, argv,
364 &argc, &argv))
359 return 2; 365 return 2;
360 366
361 if (GNUNET_OK != 367 if (GNUNET_OK !=
362 GNUNET_PROGRAM_run (argc, argv, "gnunet-nat-server [options] PORT", 368 GNUNET_PROGRAM_run (argc,
363 _("GNUnet NAT traversal test helper daemon"), options, 369 argv,
364 &run, NULL)) 370 "gnunet-nat-server [options] PORT",
371 _("GNUnet NAT traversal test helper daemon"),
372 options,
373 &run,
374 NULL))
365 { 375 {
366 GNUNET_free ((void*) argv); 376 GNUNET_free ((void*) argv);
367 return 1; 377 return 1;
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c
index 10921150d..4d0ed5723 100644
--- a/src/nat/gnunet-nat.c
+++ b/src/nat/gnunet-nat.c
@@ -59,6 +59,22 @@ static int listen_reversal;
59static int use_tcp; 59static int use_tcp;
60 60
61/** 61/**
62 * If we do auto-configuration, should we write the result
63 * to a file?
64 */
65static int write_cfg;
66
67/**
68 * Configuration filename.
69 */
70static const char *cfg_file;
71
72/**
73 * Original configuration.
74 */
75static const struct GNUNET_CONFIGURATION_Handle *cfg;
76
77/**
62 * Protocol to use. 78 * Protocol to use.
63 */ 79 */
64static uint8_t proto; 80static uint8_t proto;
@@ -149,9 +165,16 @@ auto_conf_iter (void *cls,
149 const char *option, 165 const char *option,
150 const char *value) 166 const char *value)
151{ 167{
168 struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
169
152 PRINTF ("%s: %s\n", 170 PRINTF ("%s: %s\n",
153 option, 171 option,
154 value); 172 value);
173 if (NULL != new_cfg)
174 GNUNET_CONFIGURATION_set_value_string (new_cfg,
175 section,
176 option,
177 value);
155} 178}
156 179
157 180
@@ -172,6 +195,7 @@ auto_config_cb (void *cls,
172{ 195{
173 const char *nat_type; 196 const char *nat_type;
174 char unknown_type[64]; 197 char unknown_type[64];
198 struct GNUNET_CONFIGURATION_Handle *new_cfg;
175 199
176 ah = NULL; 200 ah = NULL;
177 switch (type) 201 switch (type)
@@ -196,19 +220,69 @@ auto_config_cb (void *cls,
196 break; 220 break;
197 } 221 }
198 222
199 PRINTF ("NAT status: %s/%s\n", 223 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
200 GNUNET_NAT_status2string (result), 224 "NAT status: %s/%s\n",
201 nat_type); 225 GNUNET_NAT_status2string (result),
226 nat_type);
227
228 /* Shortcut: if there are no changes suggested, bail out early. */
229 if (GNUNET_NO ==
230 GNUNET_CONFIGURATION_is_dirty (diff))
231 {
232 test_finished ();
233 return;
234 }
202 235
236 /* Apply diff to original configuration and show changes
237 to the user */
238 new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
239
203 if (NULL != diff) 240 if (NULL != diff)
204 { 241 {
205 PRINTF ("SUGGESTED CHANGES:\n"); 242 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
243 _("Suggested configuration changes:\n"));
206 GNUNET_CONFIGURATION_iterate_section_values (diff, 244 GNUNET_CONFIGURATION_iterate_section_values (diff,
207 "nat", 245 "nat",
208 &auto_conf_iter, 246 &auto_conf_iter,
209 NULL); 247 new_cfg);
210 } 248 }
211 // FIXME: have option to save config 249
250 /* If desired, write configuration to file; we write only the
251 changes to the defaults to keep things compact. */
252 if ( (write_cfg) &&
253 (NULL != diff) )
254 {
255 struct GNUNET_CONFIGURATION_Handle *def_cfg;
256
257 GNUNET_CONFIGURATION_set_value_string (new_cfg,
258 "ARM",
259 "CONFIG",
260 NULL);
261 def_cfg = GNUNET_CONFIGURATION_create ();
262 GNUNET_break (GNUNET_OK ==
263 GNUNET_CONFIGURATION_load (def_cfg,
264 NULL));
265 if (GNUNET_OK !=
266 GNUNET_CONFIGURATION_write_diffs (def_cfg,
267 new_cfg,
268 cfg_file))
269 {
270 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
271 _("Failed to write configuration to `%s'\n"),
272 cfg_file);
273 global_ret = 1;
274 }
275 else
276 {
277 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
278 _("Wrote updated configuration to `%s'\n"),
279 cfg_file);
280 }
281 GNUNET_CONFIGURATION_destroy (def_cfg);
282 }
283
284 if (NULL != new_cfg)
285 GNUNET_CONFIGURATION_destroy (new_cfg);
212 test_finished (); 286 test_finished ();
213} 287}
214 288
@@ -385,8 +459,11 @@ run (void *cls,
385 struct sockaddr_in extern_sa; 459 struct sockaddr_in extern_sa;
386 struct sockaddr *local_sa; 460 struct sockaddr *local_sa;
387 struct sockaddr *remote_sa; 461 struct sockaddr *remote_sa;
388 size_t local_len; 462 socklen_t local_len;
389 size_t remote_len; 463 size_t remote_len;
464
465 cfg_file = cfgfile;
466 cfg = c;
390 467
391 if (use_tcp && use_udp) 468 if (use_tcp && use_udp)
392 { 469 {
@@ -450,9 +527,9 @@ run (void *cls,
450 } 527 }
451 if (NULL != local_addr) 528 if (NULL != local_addr)
452 { 529 {
453 local_len = GNUNET_STRINGS_parse_socket_addr (local_addr, 530 local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr,
454 &af, 531 &af,
455 &local_sa); 532 &local_sa);
456 if (0 == local_len) 533 if (0 == local_len)
457 { 534 {
458 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 535 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
@@ -611,14 +688,11 @@ main (int argc,
611 gettext_noop ("which external IP and port should be used to test"), 688 gettext_noop ("which external IP and port should be used to test"),
612 GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr }, 689 GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
613 {'i', "in", "ADDRESS", 690 {'i', "in", "ADDRESS",
614 gettext_noop ("which IP and port are we locally using to listen to for connection reversals"), 691 gettext_noop ("which IP and port are we locally using to bind/listen to"),
615 GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr }, 692 GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr },
616 {'r', "remote", "ADDRESS", 693 {'r', "remote", "ADDRESS",
617 gettext_noop ("which remote IP and port should be asked for connection reversal"), 694 gettext_noop ("which remote IP and port should be asked for connection reversal"),
618 GNUNET_YES, &GNUNET_GETOPT_set_string, &remote_addr }, 695 GNUNET_YES, &GNUNET_GETOPT_set_string, &remote_addr },
619 {'L', "listen", NULL,
620 gettext_noop ("listen for connection reversal requests"),
621 GNUNET_NO, &GNUNET_GETOPT_set_one, &listen_reversal },
622 {'p', "port", NULL, 696 {'p', "port", NULL,
623 gettext_noop ("port to use to advertise"), 697 gettext_noop ("port to use to advertise"),
624 GNUNET_YES, &GNUNET_GETOPT_set_uint, &adv_port }, 698 GNUNET_YES, &GNUNET_GETOPT_set_uint, &adv_port },
@@ -631,6 +705,12 @@ main (int argc,
631 {'u', "udp", NULL, 705 {'u', "udp", NULL,
632 gettext_noop ("use UDP"), 706 gettext_noop ("use UDP"),
633 GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp }, 707 GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp },
708 {'w', "write", NULL,
709 gettext_noop ("write configuration file (for autoconfiguration)"),
710 GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg },
711 {'W', "watch", NULL,
712 gettext_noop ("watch for connection reversal requests"),
713 GNUNET_NO, &GNUNET_GETOPT_set_one, &listen_reversal },
634 GNUNET_GETOPT_OPTION_END 714 GNUNET_GETOPT_OPTION_END
635 }; 715 };
636 716
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c
index 4ad6c8d2c..762175437 100644
--- a/src/nat/gnunet-service-nat.c
+++ b/src/nat/gnunet-service-nat.c
@@ -28,8 +28,11 @@
28 * knowledge about the local network topology. 28 * knowledge about the local network topology.
29 * 29 *
30 * TODO: 30 * TODO:
31 * - implement UPnPC/PMP-based NAT traversal 31 * - test ICMP based NAT traversal
32 * - implement autoconfig 32 * - implement "more" autoconfig:
33 * re-work gnunet-nat-server & integrate!
34 * - implement & test STUN processing to classify NAT;
35 * basically, open port & try different methods.
33 * - implement NEW logic for external IP detection 36 * - implement NEW logic for external IP detection
34 */ 37 */
35#include "platform.h" 38#include "platform.h"
@@ -40,6 +43,7 @@
40#include "gnunet_statistics_service.h" 43#include "gnunet_statistics_service.h"
41#include "gnunet_nat_service.h" 44#include "gnunet_nat_service.h"
42#include "gnunet-service-nat_stun.h" 45#include "gnunet-service-nat_stun.h"
46#include "gnunet-service-nat_mini.h"
43#include "gnunet-service-nat_helper.h" 47#include "gnunet-service-nat_helper.h"
44#include "nat.h" 48#include "nat.h"
45#include <gcrypt.h> 49#include <gcrypt.h>
@@ -51,6 +55,49 @@
51 */ 55 */
52#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) 56#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
53 57
58/**
59 * How long do we wait until we forcefully terminate autoconfiguration?
60 */
61#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
62
63/**
64 * How long do we wait until we re-try running `external-ip` if the
65 * command failed to terminate nicely?
66 */
67#define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
68
69/**
70 * How long do we wait until we re-try running `external-ip` if the
71 * command failed (but terminated)?
72 */
73#define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
74
75/**
76 * How long do we wait until we re-try running `external-ip` if the
77 * command succeeded?
78 */
79#define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
80
81
82/**
83 * Information we track per client address.
84 */
85struct ClientAddress
86{
87 /**
88 * Network address used by the client.
89 */
90 struct sockaddr_storage ss;
91
92 /**
93 * Handle to active UPnP request where we asked upnpc to open
94 * a port at the NAT. NULL if we do not have such a request
95 * pending.
96 */
97 struct GNUNET_NAT_MiniHandle *mh;
98
99};
100
54 101
55/** 102/**
56 * Internal data structure we track for each of our clients. 103 * Internal data structure we track for each of our clients.
@@ -81,7 +128,7 @@ struct ClientHandle
81 /** 128 /**
82 * Array of addresses used by the service. 129 * Array of addresses used by the service.
83 */ 130 */
84 struct sockaddr **addrs; 131 struct ClientAddress *caddrs;
85 132
86 /** 133 /**
87 * What does this client care about? 134 * What does this client care about?
@@ -89,7 +136,7 @@ struct ClientHandle
89 enum GNUNET_NAT_RegisterFlags flags; 136 enum GNUNET_NAT_RegisterFlags flags;
90 137
91 /** 138 /**
92 * Is any of the @e addrs in a reserved subnet for NAT? 139 * Is any of the @e caddrs in a reserved subnet for NAT?
93 */ 140 */
94 int natted_address; 141 int natted_address;
95 142
@@ -101,8 +148,9 @@ struct ClientHandle
101 148
102 /** 149 /**
103 * Number of addresses that this service is bound to. 150 * Number of addresses that this service is bound to.
151 * Length of the @e caddrs array.
104 */ 152 */
105 uint16_t num_addrs; 153 uint16_t num_caddrs;
106 154
107 /** 155 /**
108 * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. 156 * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
@@ -187,6 +235,80 @@ struct StunExternalIP
187 235
188 236
189/** 237/**
238 * Context for autoconfiguration operations.
239 */
240struct AutoconfigContext
241{
242 /**
243 * Kept in a DLL.
244 */
245 struct AutoconfigContext *prev;
246
247 /**
248 * Kept in a DLL.
249 */
250 struct AutoconfigContext *next;
251
252 /**
253 * Which client asked the question.
254 */
255 struct ClientHandle *ch;
256
257 /**
258 * Configuration we are creating.
259 */
260 struct GNUNET_CONFIGURATION_Handle *c;
261
262 /**
263 * Original configuration (for diffing).
264 */
265 struct GNUNET_CONFIGURATION_Handle *orig;
266
267 /**
268 * Timeout task to force termination.
269 */
270 struct GNUNET_SCHEDULER_Task *timeout_task;
271
272 /**
273 * What type of system are we on?
274 */
275 char *system_type;
276
277 /**
278 * Handle to activity to probe for our external IP.
279 */
280 struct GNUNET_NAT_ExternalHandle *probe_external;
281
282 /**
283 * #GNUNET_YES if upnpc should be used,
284 * #GNUNET_NO if upnpc should not be used,
285 * #GNUNET_SYSERR if we should simply not change the option.
286 */
287 int enable_upnpc;
288
289 /**
290 * Status code to return to the client.
291 */
292 enum GNUNET_NAT_StatusCode status_code;
293
294 /**
295 * NAT type to return to the client.
296 */
297 enum GNUNET_NAT_Type type;
298};
299
300
301/**
302 * DLL of our autoconfiguration operations.
303 */
304static struct AutoconfigContext *ac_head;
305
306/**
307 * DLL of our autoconfiguration operations.
308 */
309static struct AutoconfigContext *ac_tail;
310
311/**
190 * Timeout to use when STUN data is considered stale. 312 * Timeout to use when STUN data is considered stale.
191 */ 313 */
192static struct GNUNET_TIME_Relative stun_stale_timeout; 314static struct GNUNET_TIME_Relative stun_stale_timeout;
@@ -236,6 +358,29 @@ static struct StunExternalIP *se_head;
236 */ 358 */
237static struct StunExternalIP *se_tail; 359static struct StunExternalIP *se_tail;
238 360
361/**
362 * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
363 * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
364 */
365static int enable_upnp;
366
367/**
368 * Task run to obtain our external IP (if #enable_upnp is set
369 * and if we find we have a NATed IP address).
370 */
371static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
372
373/**
374 * Handle to our operation to run `external-ip`.
375 */
376static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
377
378/**
379 * What is our external IP address as claimed by `external-ip`?
380 * 0 for unknown.
381 */
382static struct in_addr mini_external_ipv4;
383
239 384
240/** 385/**
241 * Free the DLL starting at #lal_head. 386 * Free the DLL starting at #lal_head.
@@ -322,7 +467,9 @@ match_ipv4 (const char *network,
322 uint8_t bits) 467 uint8_t bits)
323{ 468{
324 struct in_addr net; 469 struct in_addr net;
325 470
471 if (0 == ip->s_addr)
472 return GNUNET_YES;
326 if (0 == bits) 473 if (0 == bits)
327 return GNUNET_YES; 474 return GNUNET_YES;
328 GNUNET_assert (1 == inet_pton (AF_INET, 475 GNUNET_assert (1 == inet_pton (AF_INET,
@@ -355,6 +502,10 @@ match_ipv6 (const char *network,
355 network, 502 network,
356 &net)); 503 &net));
357 memset (&mask, 0, sizeof (mask)); 504 memset (&mask, 0, sizeof (mask));
505 if (0 == memcmp (&mask,
506 ip,
507 sizeof (mask)))
508 return GNUNET_YES;
358 off = 0; 509 off = 0;
359 while (bits > 8) 510 while (bits > 8)
360 { 511 {
@@ -411,6 +562,634 @@ is_nat_v6 (const struct in6_addr *ip)
411 562
412 563
413/** 564/**
565 * Closure for #ifc_proc.
566 */
567struct IfcProcContext
568{
569
570 /**
571 * Head of DLL of local addresses.
572 */
573 struct LocalAddressList *lal_head;
574
575 /**
576 * Tail of DLL of local addresses.
577 */
578 struct LocalAddressList *lal_tail;
579
580};
581
582
583/**
584 * Callback function invoked for each interface found. Adds them
585 * to our new address list.
586 *
587 * @param cls a `struct IfcProcContext *`
588 * @param name name of the interface (can be NULL for unknown)
589 * @param isDefault is this presumably the default interface
590 * @param addr address of this interface (can be NULL for unknown or unassigned)
591 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
592 * @param netmask the network mask (can be NULL for unknown or unassigned)
593 * @param addrlen length of the address
594 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
595 */
596static int
597ifc_proc (void *cls,
598 const char *name,
599 int isDefault,
600 const struct sockaddr *addr,
601 const struct sockaddr *broadcast_addr,
602 const struct sockaddr *netmask,
603 socklen_t addrlen)
604{
605 struct IfcProcContext *ifc_ctx = cls;
606 struct LocalAddressList *lal;
607 size_t alen;
608 const struct in_addr *ip4;
609 const struct in6_addr *ip6;
610 enum GNUNET_NAT_AddressClass ac;
611
612 switch (addr->sa_family)
613 {
614 case AF_INET:
615 alen = sizeof (struct sockaddr_in);
616 ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
617 if (match_ipv4 ("127.0.0.0", ip4, 8))
618 ac = GNUNET_NAT_AC_LOOPBACK;
619 else if (is_nat_v4 (ip4))
620 ac = GNUNET_NAT_AC_LAN;
621 else
622 ac = GNUNET_NAT_AC_GLOBAL;
623 break;
624 case AF_INET6:
625 alen = sizeof (struct sockaddr_in6);
626 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
627 if (match_ipv6 ("::1", ip6, 128))
628 ac = GNUNET_NAT_AC_LOOPBACK;
629 else if (is_nat_v6 (ip6))
630 ac = GNUNET_NAT_AC_LAN;
631 else
632 ac = GNUNET_NAT_AC_GLOBAL;
633 if ( (ip6->s6_addr[11] == 0xFF) &&
634 (ip6->s6_addr[12] == 0xFE) )
635 {
636 /* contains a MAC, be extra careful! */
637 ac |= GNUNET_NAT_AC_PRIVATE;
638 }
639 break;
640#if AF_UNIX
641 case AF_UNIX:
642 GNUNET_break (0);
643 return GNUNET_OK;
644#endif
645 default:
646 GNUNET_break (0);
647 return GNUNET_OK;
648 }
649 lal = GNUNET_malloc (sizeof (*lal));
650 lal->af = addr->sa_family;
651 lal->ac = ac;
652 GNUNET_memcpy (&lal->addr,
653 addr,
654 alen);
655 GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
656 ifc_ctx->lal_tail,
657 lal);
658 return GNUNET_OK;
659}
660
661
662/**
663 * Notify client about a change in the list of addresses this peer
664 * has.
665 *
666 * @param ac address class of the entry in the list that changed
667 * @param ch client to contact
668 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
669 * @param addr the address that changed
670 * @param addr_len number of bytes in @a addr
671 */
672static void
673notify_client (enum GNUNET_NAT_AddressClass ac,
674 struct ClientHandle *ch,
675 int add,
676 const void *addr,
677 size_t addr_len)
678{
679 struct GNUNET_MQ_Envelope *env;
680 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
681
682 env = GNUNET_MQ_msg_extra (msg,
683 addr_len,
684 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
685 msg->add_remove = htonl (add);
686 msg->addr_class = htonl (ac);
687 GNUNET_memcpy (&msg[1],
688 addr,
689 addr_len);
690 GNUNET_MQ_send (ch->mq,
691 env);
692}
693
694
695/**
696 * Check if we should bother to notify this client about this
697 * address change, and if so, do it.
698 *
699 * @param delta the entry in the list that changed
700 * @param ch client to check
701 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
702 */
703static void
704check_notify_client (struct LocalAddressList *delta,
705 struct ClientHandle *ch,
706 int add)
707{
708 size_t alen;
709 struct sockaddr_in v4;
710 struct sockaddr_in6 v6;
711
712 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
713 return;
714 switch (delta->af)
715 {
716 case AF_INET:
717 alen = sizeof (struct sockaddr_in);
718 GNUNET_memcpy (&v4,
719 &delta->addr,
720 alen);
721 for (unsigned int i=0;i<ch->num_caddrs;i++)
722 {
723 const struct sockaddr_in *c4;
724
725 if (AF_INET != ch->caddrs[i].ss.ss_family)
726 return; /* IPv4 not relevant */
727 c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss;
728 if ( match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) &&
729 (0 != c4->sin_addr.s_addr) &&
730 (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) )
731 continue; /* bound to loopback, but this is not loopback */
732 if ( (! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) ) &&
733 (0 != c4->sin_addr.s_addr) &&
734 match_ipv4 ("127.0.0.1", &v4.sin_addr, 8) )
735 continue; /* bound to non-loopback, but this is loopback */
736 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
737 (0 != c4->sin_addr.s_addr) &&
738 (! is_nat_v4 (&v4.sin_addr)) )
739 continue; /* based on external-IP, but this IP is not
740 from private address range. */
741 if ( (0 != memcmp (&v4.sin_addr,
742 &c4->sin_addr,
743 sizeof (struct in_addr))) &&
744 (0 != c4->sin_addr.s_addr) &&
745 ( (! is_nat_v4 (&c4->sin_addr)) ||
746 (0 == (ch->flags & GNUNET_NAT_AC_EXTERN))) )
747 continue; /* this IP is not from private address range,
748 and IP does not match. */
749
750 /* OK, IP seems relevant, notify client */
751 v4.sin_port = c4->sin_port;
752 notify_client (delta->ac,
753 ch,
754 add,
755 &v4,
756 alen);
757 }
758 break;
759 case AF_INET6:
760 alen = sizeof (struct sockaddr_in6);
761 GNUNET_memcpy (&v6,
762 &delta->addr,
763 alen);
764 for (unsigned int i=0;i<ch->num_caddrs;i++)
765 {
766 const struct sockaddr_in6 *c6;
767
768 if (AF_INET6 != ch->caddrs[i].ss.ss_family)
769 return; /* IPv4 not relevant */
770 c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss;
771 if ( match_ipv6 ("::1", &c6->sin6_addr, 128) &&
772 (0 != memcmp (&c6->sin6_addr,
773 &in6addr_any,
774 sizeof (struct in6_addr))) &&
775 (! match_ipv6 ("::1", &v6.sin6_addr, 128)) )
776 continue; /* bound to loopback, but this is not loopback */
777 if ( (! match_ipv6 ("::1", &c6->sin6_addr, 128) ) &&
778 (0 != memcmp (&c6->sin6_addr,
779 &in6addr_any,
780 sizeof (struct in6_addr))) &&
781 match_ipv6 ("::1", &v6.sin6_addr, 128) )
782 continue; /* bound to non-loopback, but this is loopback */
783 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
784 (0 != memcmp (&c6->sin6_addr,
785 &in6addr_any,
786 sizeof (struct in6_addr))) &&
787 (! is_nat_v6 (&v6.sin6_addr)) )
788 continue; /* based on external-IP, but this IP is not
789 from private address range. */
790 if ( (0 != memcmp (&v6.sin6_addr,
791 &c6->sin6_addr,
792 sizeof (struct in6_addr))) &&
793 (0 != memcmp (&c6->sin6_addr,
794 &in6addr_any,
795 sizeof (struct in6_addr))) &&
796 (! is_nat_v6 (&c6->sin6_addr)) )
797 continue; /* this IP is not from private address range,
798 and IP does not match. */
799 if ( (match_ipv6 ("fe80::", &c6->sin6_addr, 10)) &&
800 (0 != memcmp (&c6->sin6_addr,
801 &in6addr_any,
802 sizeof (struct in6_addr))) &&
803 (0 != memcmp (&v6.sin6_addr,
804 &c6->sin6_addr,
805 sizeof (struct in6_addr))) &&
806 (0 == (delta->ac & GNUNET_NAT_AC_EXTERN)) )
807 continue; /* client bound to link-local, and the other address
808 does not match and is not an external IP */
809
810 /* OK, IP seems relevant, notify client */
811 v6.sin6_port = c6->sin6_port;
812 notify_client (delta->ac,
813 ch,
814 add,
815 &v6,
816 alen);
817 }
818 break;
819 default:
820 GNUNET_break (0);
821 return;
822 }
823}
824
825
826/**
827 * Notify all clients about a change in the list
828 * of addresses this peer has.
829 *
830 * @param delta the entry in the list that changed
831 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
832 */
833static void
834notify_clients (struct LocalAddressList *delta,
835 int add)
836{
837 for (struct ClientHandle *ch = ch_head;
838 NULL != ch;
839 ch = ch->next)
840 check_notify_client (delta,
841 ch,
842 add);
843}
844
845
846/**
847 * Tell relevant client about a change in our external
848 * IPv4 address.
849 *
850 * @param v4 the external address that changed
851 * @param ch client to check if it cares and possibly notify
852 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
853 */
854static void
855check_notify_client_external_ipv4_change (const struct in_addr *v4,
856 struct ClientHandle *ch,
857 int add)
858{
859 struct sockaddr_in sa;
860 uint16_t port;
861 uint16_t bport;
862
863 /* (1) check if client cares. */
864 if (! ch->natted_address)
865 return;
866 if (0 == (GNUNET_NAT_RF_ADDRESSES & ch->flags))
867 return;
868 bport = 0;
869 for (unsigned int i=0;i<ch->num_caddrs;i++)
870 {
871 const struct sockaddr_storage *ss = &ch->caddrs[i].ss;
872
873 if (AF_INET != ss->ss_family)
874 continue;
875 bport = ntohs (((const struct sockaddr_in *) ss)->sin_port);
876 }
877 if (0 == bport)
878 return; /* IPv6-only */
879
880 /* (2) figure out external port, build sockaddr */
881 port = ch->adv_port;
882 if (0 == port)
883 port = bport;
884 memset (&sa,
885 0,
886 sizeof (sa));
887 sa.sin_family = AF_INET;
888 sa.sin_addr = *v4;
889 sa.sin_port = htons (port);
890
891 /* (3) notify client of change */
892 notify_client (is_nat_v4 (v4)
893 ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN_PRIVATE
894 : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL,
895 ch,
896 add,
897 &sa,
898 sizeof (sa));
899}
900
901
902/**
903 * Tell relevant clients about a change in our external
904 * IPv4 address.
905 *
906 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
907 * @param v4 the external address that changed
908 */
909static void
910notify_clients_external_ipv4_change (int add,
911 const struct in_addr *v4)
912{
913 for (struct ClientHandle *ch = ch_head;
914 NULL != ch;
915 ch = ch->next)
916 check_notify_client_external_ipv4_change (v4,
917 ch,
918 add);
919}
920
921
922/**
923 * Task used to run `external-ip` to get our external IPv4
924 * address and pass it to NATed clients if possible.
925 *
926 * @param cls NULL
927 */
928static void
929run_external_ip (void *cls);
930
931
932/**
933 * We learn our current external IP address. If it changed,
934 * notify all of our applicable clients. Also re-schedule
935 * #run_external_ip with an appropriate timeout.
936 *
937 * @param cls NULL
938 * @param addr the address, NULL on errors
939 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
940 */
941static void
942handle_external_ip (void *cls,
943 const struct in_addr *addr,
944 enum GNUNET_NAT_StatusCode result)
945{
946 char buf[INET_ADDRSTRLEN];
947
948 probe_external_ip_op = NULL;
949 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
950 probe_external_ip_task
951 = GNUNET_SCHEDULER_add_delayed ((NULL == addr)
952 ? EXTERN_IP_RETRY_FAILURE
953 : EXTERN_IP_RETRY_SUCCESS,
954 &run_external_ip,
955 NULL);
956 switch (result)
957 {
958 case GNUNET_NAT_ERROR_SUCCESS:
959 if (addr->s_addr == mini_external_ipv4.s_addr)
960 return; /* not change */
961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 "Our external IP is now %s\n",
963 inet_ntop (AF_INET,
964 addr,
965 buf,
966 sizeof (buf)));
967 if (0 != mini_external_ipv4.s_addr)
968 notify_clients_external_ipv4_change (GNUNET_NO,
969 &mini_external_ipv4);
970 mini_external_ipv4 = *addr;
971 notify_clients_external_ipv4_change (GNUNET_YES,
972 &mini_external_ipv4);
973 break;
974 default:
975 if (0 != mini_external_ipv4.s_addr)
976 notify_clients_external_ipv4_change (GNUNET_NO,
977 &mini_external_ipv4);
978 mini_external_ipv4.s_addr = 0;
979 break;
980 }
981}
982
983
984/**
985 * Task used to run `external-ip` to get our external IPv4
986 * address and pass it to NATed clients if possible.
987 *
988 * @param cls NULL
989 */
990static void
991run_external_ip (void *cls)
992{
993 probe_external_ip_task
994 = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
995 &run_external_ip,
996 NULL);
997 if (NULL != probe_external_ip_op)
998 {
999 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
1000 probe_external_ip_op = NULL;
1001 }
1002 probe_external_ip_op
1003 = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
1004 NULL);
1005}
1006
1007
1008/**
1009 * Task we run periodically to scan for network interfaces.
1010 *
1011 * @param cls NULL
1012 */
1013static void
1014run_scan (void *cls)
1015{
1016 struct IfcProcContext ifc_ctx;
1017 int found;
1018 int have_nat;
1019
1020 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
1021 &run_scan,
1022 NULL);
1023 memset (&ifc_ctx,
1024 0,
1025 sizeof (ifc_ctx));
1026 GNUNET_OS_network_interfaces_list (&ifc_proc,
1027 &ifc_ctx);
1028 /* remove addresses that disappeared */
1029 for (struct LocalAddressList *lal = lal_head;
1030 NULL != lal;
1031 lal = lal->next)
1032 {
1033 found = GNUNET_NO;
1034 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1035 NULL != pos;
1036 pos = pos->next)
1037 {
1038 if ( (pos->af == lal->af) &&
1039 (0 == memcmp (&lal->addr,
1040 &pos->addr,
1041 (AF_INET == lal->af)
1042 ? sizeof (struct sockaddr_in)
1043 : sizeof (struct sockaddr_in6))) )
1044 found = GNUNET_YES;
1045 }
1046 if (GNUNET_NO == found)
1047 notify_clients (lal,
1048 GNUNET_NO);
1049 }
1050
1051 /* add addresses that appeared */
1052 have_nat = GNUNET_NO;
1053 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1054 NULL != pos;
1055 pos = pos->next)
1056 {
1057 found = GNUNET_NO;
1058 if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac))
1059 have_nat = GNUNET_YES;
1060 for (struct LocalAddressList *lal = lal_head;
1061 NULL != lal;
1062 lal = lal->next)
1063 {
1064 if ( (pos->af == lal->af) &&
1065 (0 == memcmp (&lal->addr,
1066 &pos->addr,
1067 (AF_INET == lal->af)
1068 ? sizeof (struct sockaddr_in)
1069 : sizeof (struct sockaddr_in6))) )
1070 found = GNUNET_YES;
1071 }
1072 if (GNUNET_NO == found)
1073 notify_clients (pos,
1074 GNUNET_YES);
1075 }
1076 if ( (GNUNET_YES == have_nat) &&
1077 (GNUNET_YES == enable_upnp) &&
1078 (NULL == probe_external_ip_task) &&
1079 (NULL == probe_external_ip_op) )
1080 {
1081 probe_external_ip_task
1082 = GNUNET_SCHEDULER_add_now (&run_external_ip,
1083 NULL);
1084 }
1085 if ( (GNUNET_NO == have_nat) &&
1086 (GNUNET_YES == enable_upnp) )
1087 {
1088 if (NULL != probe_external_ip_task)
1089 {
1090 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
1091 probe_external_ip_task = NULL;
1092 }
1093 if (NULL != probe_external_ip_op)
1094 {
1095 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
1096 probe_external_ip_op = NULL;
1097 }
1098 }
1099
1100 destroy_lal ();
1101 lal_head = ifc_ctx.lal_head;
1102 lal_tail = ifc_ctx.lal_tail;
1103}
1104
1105
1106/**
1107 * Function called whenever our set of external addresses
1108 * as created by `upnpc` changes.
1109 *
1110 * @param cls closure with our `struct ClientHandle *`
1111 * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
1112 * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
1113 * @param addr either the previous or the new public IP address
1114 * @param addrlen actual length of the @a addr
1115 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
1116 */
1117static void
1118upnp_addr_change_cb (void *cls,
1119 int add_remove,
1120 const struct sockaddr *addr,
1121 socklen_t addrlen,
1122 enum GNUNET_NAT_StatusCode result)
1123{
1124 struct ClientHandle *ch = cls;
1125 enum GNUNET_NAT_AddressClass ac;
1126
1127 switch (result)
1128 {
1129 case GNUNET_NAT_ERROR_SUCCESS:
1130 GNUNET_assert (NULL != addr);
1131 break;
1132 case GNUNET_NAT_ERROR_UPNPC_FAILED:
1133 case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
1134 case GNUNET_NAT_ERROR_IPC_FAILURE:
1135 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1136 "Running upnpc failed: %d\n",
1137 result);
1138 return;
1139 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
1140 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1141 "external-ip binary not found\n");
1142 return;
1143 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
1144 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1145 "external-ip binary could not be run\n");
1146 return;
1147 case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
1148 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1149 "upnpc failed to create port mapping\n");
1150 return;
1151 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "Invalid output from upnpc\n");
1154 return;
1155 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157 "Invalid address returned by upnpc\n");
1158 return;
1159 default:
1160 GNUNET_break (0); /* should not be possible */
1161 return;
1162 }
1163 switch (addr->sa_family)
1164 {
1165 case AF_INET:
1166 ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr)
1167 ? GNUNET_NAT_AC_LAN_PRIVATE
1168 : GNUNET_NAT_AC_EXTERN;
1169 break;
1170 case AF_INET6:
1171 ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr)
1172 ? GNUNET_NAT_AC_LAN_PRIVATE
1173 : GNUNET_NAT_AC_EXTERN;
1174 break;
1175 default:
1176 GNUNET_break (0);
1177 return;
1178 }
1179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1180 "upnp external address %s: %s\n",
1181 add_remove ? "added" : "removed",
1182 GNUNET_a2s (addr,
1183 addrlen));
1184 notify_client (ac,
1185 ch,
1186 add_remove,
1187 addr,
1188 addrlen);
1189}
1190
1191
1192/**
414 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. 1193 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
415 * We remember the client for updates upon future NAT events. 1194 * We remember the client for updates upon future NAT events.
416 * 1195 *
@@ -426,7 +1205,7 @@ handle_register (void *cls,
426 size_t left; 1205 size_t left;
427 1206
428 if ( (0 != ch->proto) || 1207 if ( (0 != ch->proto) ||
429 (NULL != ch->addrs) ) 1208 (NULL != ch->caddrs) )
430 { 1209 {
431 /* double registration not allowed */ 1210 /* double registration not allowed */
432 GNUNET_break (0); 1211 GNUNET_break (0);
@@ -438,15 +1217,17 @@ handle_register (void *cls,
438 ch->flags = message->flags; 1217 ch->flags = message->flags;
439 ch->proto = message->proto; 1218 ch->proto = message->proto;
440 ch->adv_port = ntohs (message->adv_port); 1219 ch->adv_port = ntohs (message->adv_port);
441 ch->num_addrs = ntohs (message->adv_port); 1220 ch->num_caddrs = ntohs (message->num_addrs);
442 ch->addrs = GNUNET_new_array (ch->num_addrs, 1221 ch->caddrs = GNUNET_new_array (ch->num_caddrs,
443 struct sockaddr *); 1222 struct ClientAddress);
444 left = ntohs (message->header.size) - sizeof (*message); 1223 left = ntohs (message->header.size) - sizeof (*message);
445 off = (const char *) &message[1]; 1224 off = (const char *) &message[1];
446 for (unsigned int i=0;i<ch->num_addrs;i++) 1225 for (unsigned int i=0;i<ch->num_caddrs;i++)
447 { 1226 {
448 size_t alen; 1227 size_t alen;
449 const struct sockaddr *sa = (const struct sockaddr *) off; 1228 const struct sockaddr *sa = (const struct sockaddr *) off;
1229 uint16_t port;
1230 int is_nat;
450 1231
451 if (sizeof (sa_family_t) > left) 1232 if (sizeof (sa_family_t) > left)
452 { 1233 {
@@ -454,6 +1235,7 @@ handle_register (void *cls,
454 GNUNET_SERVICE_client_drop (ch->client); 1235 GNUNET_SERVICE_client_drop (ch->client);
455 return; 1236 return;
456 } 1237 }
1238 is_nat = GNUNET_NO;
457 switch (sa->sa_family) 1239 switch (sa->sa_family)
458 { 1240 {
459 case AF_INET: 1241 case AF_INET:
@@ -462,7 +1244,8 @@ handle_register (void *cls,
462 1244
463 alen = sizeof (struct sockaddr_in); 1245 alen = sizeof (struct sockaddr_in);
464 if (is_nat_v4 (&s4->sin_addr)) 1246 if (is_nat_v4 (&s4->sin_addr))
465 ch->natted_address = GNUNET_YES; 1247 is_nat = GNUNET_YES;
1248 port = ntohs (s4->sin_port);
466 } 1249 }
467 break; 1250 break;
468 case AF_INET6: 1251 case AF_INET6:
@@ -471,12 +1254,14 @@ handle_register (void *cls,
471 1254
472 alen = sizeof (struct sockaddr_in6); 1255 alen = sizeof (struct sockaddr_in6);
473 if (is_nat_v6 (&s6->sin6_addr)) 1256 if (is_nat_v6 (&s6->sin6_addr))
474 ch->natted_address = GNUNET_YES; 1257 is_nat = GNUNET_YES;
1258 port = ntohs (s6->sin6_port);
475 } 1259 }
476 break; 1260 break;
477#if AF_UNIX 1261#if AF_UNIX
478 case AF_UNIX: 1262 case AF_UNIX:
479 alen = sizeof (struct sockaddr_un); 1263 alen = sizeof (struct sockaddr_un);
1264 port = 0;
480 break; 1265 break;
481#endif 1266#endif
482 default: 1267 default:
@@ -484,13 +1269,45 @@ handle_register (void *cls,
484 GNUNET_SERVICE_client_drop (ch->client); 1269 GNUNET_SERVICE_client_drop (ch->client);
485 return; 1270 return;
486 } 1271 }
1272 /* store address */
487 GNUNET_assert (alen <= left); 1273 GNUNET_assert (alen <= left);
488 ch->addrs[i] = GNUNET_malloc (alen); 1274 GNUNET_assert (alen <= sizeof (struct sockaddr_storage));
489 GNUNET_memcpy (ch->addrs[i], 1275 GNUNET_memcpy (&ch->caddrs[i].ss,
490 sa, 1276 sa,
491 alen); 1277 alen);
1278
1279 /* If applicable, try UPNPC NAT punching */
1280 if ( (is_nat) &&
1281 (enable_upnp) &&
1282 ( (IPPROTO_TCP == ch->proto) ||
1283 (IPPROTO_UDP == ch->proto) ) )
1284 {
1285 ch->natted_address = GNUNET_YES;
1286 ch->caddrs[i].mh
1287 = GNUNET_NAT_mini_map_start (port,
1288 IPPROTO_TCP == ch->proto,
1289 &upnp_addr_change_cb,
1290 ch);
1291 }
1292
492 off += alen; 1293 off += alen;
493 } 1294 }
1295 /* Actually send IP address list to client */
1296 for (struct LocalAddressList *lal = lal_head;
1297 NULL != lal;
1298 lal = lal->next)
1299 {
1300 check_notify_client (lal,
1301 ch,
1302 GNUNET_YES);
1303 }
1304 /* Also consider IPv4 determined by `external-ip` */
1305 if (0 != mini_external_ipv4.s_addr)
1306 {
1307 check_notify_client_external_ipv4_change (&mini_external_ipv4,
1308 ch,
1309 GNUNET_YES);
1310 }
494 GNUNET_SERVICE_client_continue (ch->client); 1311 GNUNET_SERVICE_client_continue (ch->client);
495} 1312}
496 1313
@@ -551,7 +1368,7 @@ notify_clients_stun_change (const struct sockaddr_in *ip,
551 sizeof (v4), 1368 sizeof (v4),
552 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); 1369 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
553 msg->add_remove = htonl ((int32_t) add); 1370 msg->add_remove = htonl ((int32_t) add);
554 msg->addr_class = htonl (GNUNET_NAT_AC_GLOBAL_EXTERN | 1371 msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN |
555 GNUNET_NAT_AC_GLOBAL); 1372 GNUNET_NAT_AC_GLOBAL);
556 GNUNET_memcpy (&msg[1], 1373 GNUNET_memcpy (&msg[1],
557 &v4, 1374 &v4,
@@ -826,344 +1643,311 @@ check_autoconfig_request (void *cls,
826 1643
827 1644
828/** 1645/**
829 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from 1646 * Stop all pending activities with respect to the @a ac
830 * client.
831 * 1647 *
832 * @param cls client who sent the message 1648 * @param ac autoconfiguration to terminate activities for
833 * @param message the message received
834 */ 1649 */
835static void 1650static void
836handle_autoconfig_request (void *cls, 1651terminate_ac_activities (struct AutoconfigContext *ac)
837 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
838{ 1652{
839 struct ClientHandle *ch = cls; 1653 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 { 1654 {
850 GNUNET_break (0); 1655 GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external);
851 GNUNET_SERVICE_client_drop (ch->client); 1656 ac->probe_external = NULL;
852 return; 1657 }
1658 if (NULL != ac->timeout_task)
1659 {
1660 GNUNET_SCHEDULER_cancel (ac->timeout_task);
1661 ac->timeout_task = NULL;
853 } 1662 }
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} 1663}
860 1664
861 1665
862/** 1666/**
863 * Task run during shutdown. 1667 * Finish handling the autoconfiguration request and send
1668 * the response to the client.
864 * 1669 *
865 * @param cls unused 1670 * @param cls the `struct AutoconfigContext` to conclude
866 */ 1671 */
867static void 1672static void
868shutdown_task (void *cls) 1673conclude_autoconfig_request (void *cls)
869{ 1674{
870 struct StunExternalIP *se; 1675 struct AutoconfigContext *ac = cls;
1676 struct ClientHandle *ch = ac->ch;
1677 struct GNUNET_NAT_AutoconfigResultMessage *arm;
1678 struct GNUNET_MQ_Envelope *env;
1679 size_t c_size;
1680 char *buf;
1681 struct GNUNET_CONFIGURATION_Handle *diff;
1682
1683 ac->timeout_task = NULL;
1684 terminate_ac_activities (ac);
1685
1686 /* Send back response */
1687 diff = GNUNET_CONFIGURATION_get_diff (ac->orig,
1688 ac->c);
1689 buf = GNUNET_CONFIGURATION_serialize (diff,
1690 &c_size);
1691 GNUNET_CONFIGURATION_destroy (diff);
1692 env = GNUNET_MQ_msg_extra (arm,
1693 c_size,
1694 GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
1695 arm->status_code = htonl ((uint32_t) ac->status_code);
1696 arm->type = htonl ((uint32_t) ac->type);
1697 GNUNET_memcpy (&arm[1],
1698 buf,
1699 c_size);
1700 GNUNET_free (buf);
1701 GNUNET_MQ_send (ch->mq,
1702 env);
871 1703
872 while (NULL != (se = se_head)) 1704 /* clean up */
873 { 1705 GNUNET_free (ac->system_type);
874 GNUNET_CONTAINER_DLL_remove (se_head, 1706 GNUNET_CONFIGURATION_destroy (ac->orig);
875 se_tail, 1707 GNUNET_CONFIGURATION_destroy (ac->c);
876 se); 1708 GNUNET_CONTAINER_DLL_remove (ac_head,
877 GNUNET_SCHEDULER_cancel (se->timeout_task); 1709 ac_tail,
878 GNUNET_free (se); 1710 ac);
879 } 1711 GNUNET_free (ac);
880 if (NULL != scan_task) 1712 GNUNET_SERVICE_client_continue (ch->client);
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} 1713}
892 1714
893 1715
894/** 1716/**
895 * Closure for #ifc_proc. 1717 * Check if all autoconfiguration operations have concluded,
1718 * and if they have, send the result back to the client.
1719 *
1720 * @param ac autoconfiguation context to check
896 */ 1721 */
897struct IfcProcContext 1722static void
1723check_autoconfig_finished (struct AutoconfigContext *ac)
898{ 1724{
899 1725 if (NULL != ac->probe_external)
900 /** 1726 return;
901 * Head of DLL of local addresses. 1727 GNUNET_SCHEDULER_cancel (ac->timeout_task);
902 */ 1728 ac->timeout_task
903 struct LocalAddressList *lal_head; 1729 = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
904 1730 ac);
905 /** 1731}
906 * Tail of DLL of local addresses.
907 */
908 struct LocalAddressList *lal_tail;
909
910};
911 1732
912 1733
913/** 1734/**
914 * Callback function invoked for each interface found. Adds them 1735 * Update ENABLE_UPNPC configuration option.
915 * to our new address list.
916 * 1736 *
917 * @param cls a `struct IfcProcContext *` 1737 * @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 */ 1738 */
926static int 1739static void
927ifc_proc (void *cls, 1740update_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{ 1741{
935 struct IfcProcContext *ifc_ctx = cls; 1742 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 { 1743 {
944 case AF_INET: 1744 case GNUNET_YES:
945 alen = sizeof (struct sockaddr_in); 1745 GNUNET_CONFIGURATION_set_value_string (ac->c,
946 ip4 = &((const struct sockaddr_in *) addr)->sin_addr; 1746 "NAT",
947 if (match_ipv4 ("127.0.0.0", ip4, 8)) 1747 "ENABLE_UPNP",
948 ac = GNUNET_NAT_AC_LOOPBACK; 1748 "YES");
949 else if (is_nat_v4 (ip4))
950 ac = GNUNET_NAT_AC_LAN;
951 else
952 ac = GNUNET_NAT_AC_GLOBAL;
953 break; 1749 break;
954 case AF_INET6: 1750 case GNUNET_NO:
955 alen = sizeof (struct sockaddr_in6); 1751 GNUNET_CONFIGURATION_set_value_string (ac->c,
956 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; 1752 "NAT",
957 if (match_ipv6 ("::1", ip6, 128)) 1753 "ENABLE_UPNP",
958 ac = GNUNET_NAT_AC_LOOPBACK; 1754 "NO");
959 else if (is_nat_v6 (ip6)) 1755 break;
960 ac = GNUNET_NAT_AC_LAN; 1756 case GNUNET_SYSERR:
961 else 1757 /* 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; 1758 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 } 1759 }
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} 1760}
990 1761
991 1762
992/** 1763/**
993 * Notify client about a change in the list 1764 * Handle result from external IP address probe during
994 * of addresses this peer has. 1765 * autoconfiguration.
995 * 1766 *
996 * @param delta the entry in the list that changed 1767 * @param cls our `struct AutoconfigContext`
997 * @param ch client to contact 1768 * @param addr the address, NULL on errors
998 * @param add #GNUNET_YES to add, #GNUNET_NO to remove 1769 * @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 */ 1770 */
1002static void 1771static void
1003notify_client (struct LocalAddressList *delta, 1772auto_external_result_cb (void *cls,
1004 struct ClientHandle *ch, 1773 const struct in_addr *addr,
1005 int add, 1774 enum GNUNET_NAT_StatusCode result)
1006 const void *addr,
1007 size_t addr_len)
1008{ 1775{
1009 struct GNUNET_MQ_Envelope *env; 1776 struct AutoconfigContext *ac = cls;
1010 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
1011 1777
1012 env = GNUNET_MQ_msg_extra (msg, 1778 ac->probe_external = NULL;
1013 addr_len, 1779 switch (result)
1014 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); 1780 {
1015 msg->add_remove = htonl (add); 1781 case GNUNET_NAT_ERROR_SUCCESS:
1016 msg->addr_class = htonl (delta->ac); 1782 ac->enable_upnpc = GNUNET_YES;
1017 GNUNET_memcpy (&msg[1], 1783 break;
1018 addr, 1784 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1019 addr_len); 1785 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1020 GNUNET_MQ_send (ch->mq, 1786 case GNUNET_NAT_ERROR_IPC_FAILURE:
1021 env); 1787 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1022} 1788 "Disabling UPNPC: %d\n",
1789 (int) result);
1790 ac->enable_upnpc = GNUNET_NO; /* did not work */
1791 break;
1792 default:
1793 GNUNET_break (0); /* unexpected */
1794 ac->enable_upnpc = GNUNET_SYSERR;
1795 break;
1796 }
1797 update_enable_upnpc_option (ac);
1798 check_autoconfig_finished (ac);
1799}
1023 1800
1024 1801
1025/** 1802/**
1026 * Notify all clients about a change in the list 1803 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
1027 * of addresses this peer has. 1804 * client.
1028 * 1805 *
1029 * @param delta the entry in the list that changed 1806 * @param cls client who sent the message
1030 * @param add #GNUNET_YES to add, #GNUNET_NO to remove 1807 * @param message the message received
1031 */ 1808 */
1032static void 1809static void
1033notify_clients (struct LocalAddressList *delta, 1810handle_autoconfig_request (void *cls,
1034 int add) 1811 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
1035{ 1812{
1036 for (struct ClientHandle *ch = ch_head; 1813 struct ClientHandle *ch = cls;
1037 NULL != ch; 1814 size_t left = ntohs (message->header.size) - sizeof (*message);
1038 ch = ch->next) 1815 struct LocalAddressList *lal;
1816 struct AutoconfigContext *ac;
1817
1818 ac = GNUNET_new (struct AutoconfigContext);
1819 ac->status_code = GNUNET_NAT_ERROR_SUCCESS;
1820 ac->ch = ch;
1821 ac->c = GNUNET_CONFIGURATION_create ();
1822 if (GNUNET_OK !=
1823 GNUNET_CONFIGURATION_deserialize (ac->c,
1824 (const char *) &message[1],
1825 left,
1826 GNUNET_NO))
1039 { 1827 {
1040 size_t alen; 1828 GNUNET_break (0);
1041 struct sockaddr_in v4; 1829 GNUNET_SERVICE_client_drop (ch->client);
1042 struct sockaddr_in6 v6; 1830 GNUNET_CONFIGURATION_destroy (ac->c);
1043 1831 GNUNET_free (ac);
1044 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) 1832 return;
1045 continue; 1833 }
1046 switch (delta->af) 1834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1835 "Received REQUEST_AUTO_CONFIG message from client\n");
1836
1837 if (GNUNET_OK !=
1838 GNUNET_CONFIGURATION_get_value_string (ac->c,
1839 "PEER",
1840 "SYSTEM_TYPE",
1841 &ac->system_type))
1842 ac->system_type = GNUNET_strdup ("UNKNOWN");
1843
1844 GNUNET_CONTAINER_DLL_insert (ac_head,
1845 ac_tail,
1846 ac);
1847 ac->orig
1848 = GNUNET_CONFIGURATION_dup (ac->c);
1849 ac->timeout_task
1850 = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
1851 &conclude_autoconfig_request,
1852 ac);
1853 ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
1854
1855 /* Probe for upnpc */
1856 if (GNUNET_SYSERR ==
1857 GNUNET_OS_check_helper_binary ("upnpc",
1858 GNUNET_NO,
1859 NULL))
1860 {
1861 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1862 _("UPnP client `upnpc` command not found, disabling UPnP\n"));
1863 ac->enable_upnpc = GNUNET_NO;
1864 }
1865 else
1866 {
1867 for (lal = lal_head; NULL != lal; lal = lal->next)
1868 if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN))
1869 /* we are behind NAT, useful to try upnpc */
1870 ac->enable_upnpc = GNUNET_YES;
1871 }
1872 if (GNUNET_YES == ac->enable_upnpc)
1873 {
1874 /* If we are a mobile device, always leave it on as the network
1875 may change to one that supports UPnP anytime. If we are
1876 stationary, check if our network actually supports UPnP, and if
1877 not, disable it. */
1878 if ( (0 == strcasecmp (ac->system_type,
1879 "INFRASTRUCTURE")) ||
1880 (0 == strcasecmp (ac->system_type,
1881 "DESKTOP")) )
1047 { 1882 {
1048 case AF_INET: 1883 /* Check if upnpc gives us an external IP */
1049 alen = sizeof (struct sockaddr_in); 1884 ac->probe_external
1050 GNUNET_memcpy (&v4, 1885 = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb,
1051 &delta->addr, 1886 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 } 1887 }
1092 } 1888 }
1889 if (NULL == ac->probe_external)
1890 update_enable_upnpc_option (ac);
1891
1892 /* Finally, check if we are already done */
1893 check_autoconfig_finished (ac);
1093} 1894}
1094 1895
1095 1896
1096/** 1897/**
1097 * Task we run periodically to scan for network interfaces. 1898 * Task run during shutdown.
1098 * 1899 *
1099 * @param cls NULL 1900 * @param cls unused
1100 */ 1901 */
1101static void 1902static void
1102run_scan (void *cls) 1903shutdown_task (void *cls)
1103{ 1904{
1104 struct IfcProcContext ifc_ctx; 1905 struct StunExternalIP *se;
1105 int found; 1906 struct AutoconfigContext *ac;
1106 1907
1107 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, 1908 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 { 1909 {
1119 found = GNUNET_NO; 1910 GNUNET_CONTAINER_DLL_remove (ac_head,
1120 for (struct LocalAddressList *pos = ifc_ctx.lal_head; 1911 ac_tail,
1121 NULL != pos; 1912 ac);
1122 pos = pos->next) 1913 terminate_ac_activities (ac);
1123 { 1914 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 } 1915 }
1136 1916 while (NULL != (se = se_head))
1137 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1138 NULL != pos;
1139 pos = pos->next)
1140 { 1917 {
1141 found = GNUNET_NO; 1918 GNUNET_CONTAINER_DLL_remove (se_head,
1142 for (struct LocalAddressList *lal = lal_head; 1919 se_tail,
1143 NULL != lal; 1920 se);
1144 lal = lal->next) 1921 GNUNET_SCHEDULER_cancel (se->timeout_task);
1145 { 1922 GNUNET_free (se);
1146 if ( (pos->af == lal->af) && 1923 }
1147 (0 == memcmp (&lal->addr, 1924 if (NULL != probe_external_ip_task)
1148 &pos->addr, 1925 {
1149 (AF_INET == lal->af) 1926 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
1150 ? sizeof (struct sockaddr_in) 1927 probe_external_ip_task = NULL;
1151 : sizeof (struct sockaddr_in6))) ) 1928 }
1152 found = GNUNET_YES; 1929 if (NULL != probe_external_ip_op)
1153 } 1930 {
1154 if (GNUNET_NO == found) 1931 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
1155 notify_clients (pos, 1932 probe_external_ip_op = NULL;
1156 GNUNET_YES); 1933 }
1934 if (NULL != scan_task)
1935 {
1936 GNUNET_SCHEDULER_cancel (scan_task);
1937 scan_task = NULL;
1938 }
1939 if (NULL != stats)
1940 {
1941 GNUNET_STATISTICS_destroy (stats,
1942 GNUNET_NO);
1943 stats = NULL;
1157 } 1944 }
1158
1159 destroy_lal (); 1945 destroy_lal ();
1160 lal_head = ifc_ctx.lal_head;
1161 lal_tail = ifc_ctx.lal_tail;
1162} 1946}
1163 1947
1164 1948
1165/** 1949/**
1166 * Handle network size estimate clients. 1950 * Setup NAT service.
1167 * 1951 *
1168 * @param cls closure 1952 * @param cls closure
1169 * @param c configuration to use 1953 * @param c configuration to use
@@ -1181,6 +1965,26 @@ run (void *cls,
1181 "STUN_STALE", 1965 "STUN_STALE",
1182 &stun_stale_timeout)) 1966 &stun_stale_timeout))
1183 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; 1967 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
1968
1969 /* Check for UPnP */
1970 enable_upnp
1971 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1972 "NAT",
1973 "ENABLE_UPNP");
1974 if (GNUNET_YES == enable_upnp)
1975 {
1976 /* check if it works */
1977 if (GNUNET_SYSERR ==
1978 GNUNET_OS_check_helper_binary ("upnpc",
1979 GNUNET_NO,
1980 NULL))
1981 {
1982 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1983 _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
1984 enable_upnp = GNUNET_SYSERR;
1985 }
1986 }
1987
1184 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 1988 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1185 NULL); 1989 NULL);
1186 stats = GNUNET_STATISTICS_create ("nat", 1990 stats = GNUNET_STATISTICS_create ("nat",
@@ -1232,9 +2036,15 @@ client_disconnect_cb (void *cls,
1232 GNUNET_CONTAINER_DLL_remove (ch_head, 2036 GNUNET_CONTAINER_DLL_remove (ch_head,
1233 ch_tail, 2037 ch_tail,
1234 ch); 2038 ch);
1235 for (unsigned int i=0;i<ch->num_addrs;i++) 2039 for (unsigned int i=0;i<ch->num_caddrs;i++)
1236 GNUNET_free_non_null (ch->addrs[i]); 2040 {
1237 GNUNET_free_non_null (ch->addrs); 2041 if (NULL != ch->caddrs[i].mh)
2042 {
2043 GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh);
2044 ch->caddrs[i].mh = NULL;
2045 }
2046 }
2047 GNUNET_free_non_null (ch->caddrs);
1238 GNUNET_free (ch); 2048 GNUNET_free (ch);
1239} 2049}
1240 2050
diff --git a/src/nat/gnunet-service-nat_mini.c b/src/nat/gnunet-service-nat_mini.c
new file mode 100644
index 000000000..e5b9d021b
--- /dev/null
+++ b/src/nat/gnunet-service-nat_mini.c
@@ -0,0 +1,763 @@
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 */
55struct 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 */
111static void
112read_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 */
160static void
161signal_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 */
180struct GNUNET_NAT_ExternalHandle *
181GNUNET_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 */
250void
251GNUNET_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 */
279struct 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 */
346static void
347do_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 */
356static void
357process_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 */
366static void
367run_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 */
404static void
405process_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 */
513static void
514do_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 */
564static void
565process_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,
582 0,
583 GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
584 if (NULL == mini->refresh_task)
585 mini->refresh_task
586 = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
587 &do_refresh,
588 mini);
589 return;
590 }
591 /*
592 * The upnpc output we're after looks like this:
593 *
594 * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
595 */
596 if ((NULL == (ipaddr = strstr (line, " "))) ||
597 (NULL == (pstr = strstr (ipaddr, ":"))) ||
598 (1 != SSCANF (pstr + 1, "%u", &port)))
599 {
600 return; /* skip line */
601 }
602 ipa = GNUNET_strdup (ipaddr + 1);
603 strstr (ipa, ":")[0] = '\0';
604 if (1 != inet_pton (AF_INET,
605 ipa,
606 &mini->current_addr.sin_addr))
607 {
608 GNUNET_free (ipa);
609 return; /* skip line */
610 }
611 GNUNET_free (ipa);
612
613 mini->current_addr.sin_port = htons (port);
614 mini->current_addr.sin_family = AF_INET;
615#if HAVE_SOCKADDR_IN_SIN_LEN
616 mini->current_addr.sin_len = sizeof (struct sockaddr_in);
617#endif
618 mini->did_map = GNUNET_YES;
619 mini->ac (mini->ac_cls,
620 GNUNET_YES,
621 (const struct sockaddr *) &mini->current_addr,
622 sizeof (mini->current_addr),
623 GNUNET_NAT_ERROR_SUCCESS);
624}
625
626
627/**
628 * Start mapping the given port using (mini)upnpc. This function
629 * should typically not be used directly (it is used within the
630 * general-purpose #GNUNET_NAT_register() code). However, it can be
631 * used if specifically UPnP-based NAT traversal is to be used or
632 * tested.
633 *
634 * @param port port to map
635 * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
636 * @param ac function to call with mapping result
637 * @param ac_cls closure for @a ac
638 * @return NULL on error (no 'upnpc' installed)
639 */
640struct GNUNET_NAT_MiniHandle *
641GNUNET_NAT_mini_map_start (uint16_t port,
642 int is_tcp,
643 GNUNET_NAT_MiniAddressCallback ac,
644 void *ac_cls)
645{
646 struct GNUNET_NAT_MiniHandle *ret;
647
648 if (GNUNET_SYSERR ==
649 GNUNET_OS_check_helper_binary ("upnpc",
650 GNUNET_NO,
651 NULL))
652 {
653 LOG (GNUNET_ERROR_TYPE_INFO,
654 _("`upnpc' command not found\n"));
655 ac (ac_cls,
656 GNUNET_SYSERR,
657 NULL, 0,
658 GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
659 return NULL;
660 }
661 LOG (GNUNET_ERROR_TYPE_DEBUG,
662 "Running `upnpc' to install mapping\n");
663 ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
664 ret->ac = ac;
665 ret->ac_cls = ac_cls;
666 ret->is_tcp = is_tcp;
667 ret->port = port;
668 ret->refresh_task =
669 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
670 &do_refresh,
671 ret);
672 run_upnpc_r (ret);
673 return ret;
674}
675
676
677/**
678 * Process output from our 'unmap' command.
679 *
680 * @param cls the `struct GNUNET_NAT_MiniHandle`
681 * @param line line of output, NULL at the end
682 */
683static void
684process_unmap_output (void *cls,
685 const char *line)
686{
687 struct GNUNET_NAT_MiniHandle *mini = cls;
688
689 if (NULL == line)
690 {
691 LOG (GNUNET_ERROR_TYPE_DEBUG,
692 "UPnP unmap done\n");
693 GNUNET_OS_command_stop (mini->unmap_cmd);
694 mini->unmap_cmd = NULL;
695 GNUNET_free (mini);
696 return;
697 }
698 /* we don't really care about the output... */
699}
700
701
702/**
703 * Remove a mapping created with (mini)upnpc. Calling
704 * this function will give 'upnpc' 1s to remove tha mapping,
705 * so while this function is non-blocking, a task will be
706 * left with the scheduler for up to 1s past this call.
707 *
708 * @param mini the handle
709 */
710void
711GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
712{
713 char pstr[6];
714
715 if (NULL != mini->refresh_task)
716 {
717 GNUNET_SCHEDULER_cancel (mini->refresh_task);
718 mini->refresh_task = NULL;
719 }
720 if (NULL != mini->refresh_cmd)
721 {
722 GNUNET_OS_command_stop (mini->refresh_cmd);
723 mini->refresh_cmd = NULL;
724 }
725 if (NULL != mini->map_cmd)
726 {
727 GNUNET_OS_command_stop (mini->map_cmd);
728 mini->map_cmd = NULL;
729 }
730 if (GNUNET_NO == mini->did_map)
731 {
732 GNUNET_free (mini);
733 return;
734 }
735 mini->ac (mini->ac_cls,
736 GNUNET_NO,
737 (const struct sockaddr *) &mini->current_addr,
738 sizeof (mini->current_addr),
739 GNUNET_NAT_ERROR_SUCCESS);
740 /* Note: oddly enough, deletion uses the external port whereas
741 * addition uses the internal port; this rarely matters since they
742 * often are the same, but it might... */
743 GNUNET_snprintf (pstr,
744 sizeof (pstr),
745 "%u",
746 (unsigned int) ntohs (mini->current_addr.sin_port));
747 LOG (GNUNET_ERROR_TYPE_DEBUG,
748 "Unmapping port %u with UPnP\n",
749 ntohs (mini->current_addr.sin_port));
750 mini->unmap_cmd
751 = GNUNET_OS_command_run (&process_unmap_output,
752 mini,
753 UNMAP_TIMEOUT,
754 "upnpc",
755 "upnpc",
756 "-d",
757 pstr,
758 mini->is_tcp ? "tcp" : "udp",
759 NULL);
760}
761
762
763/* 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 */
37typedef 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 */
46struct 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 */
56struct GNUNET_NAT_ExternalHandle *
57GNUNET_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 */
66void
67GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh);
68
69
70/**
71 * Handle to a mapping created with upnpc.
72 */
73struct 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 */
87typedef 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 */
108struct GNUNET_NAT_MiniHandle *
109GNUNET_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 */
123void
124GNUNET_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..481bc6fde 100644
--- a/src/nat/nat_api.c
+++ b/src/nat/nat_api.c
@@ -49,6 +49,11 @@ struct AddrEntry
49 struct AddrEntry *prev; 49 struct AddrEntry *prev;
50 50
51 /** 51 /**
52 * Address class of the address.
53 */
54 enum GNUNET_NAT_AddressClass ac;
55
56 /**
52 * Number of bytes that follow. 57 * Number of bytes that follow.
53 */ 58 */
54 socklen_t addrlen; 59 socklen_t addrlen;
@@ -130,11 +135,25 @@ do_connect (void *cls);
130static void 135static void
131reconnect (struct GNUNET_NAT_Handle *nh) 136reconnect (struct GNUNET_NAT_Handle *nh)
132{ 137{
138 struct AddrEntry *ae;
139
133 if (NULL != nh->mq) 140 if (NULL != nh->mq)
134 { 141 {
135 GNUNET_MQ_destroy (nh->mq); 142 GNUNET_MQ_destroy (nh->mq);
136 nh->mq = NULL; 143 nh->mq = NULL;
137 } 144 }
145 while (NULL != (ae = nh->ae_head))
146 {
147 GNUNET_CONTAINER_DLL_remove (nh->ae_head,
148 nh->ae_tail,
149 ae);
150 nh->address_callback (nh->callback_cls,
151 GNUNET_NO,
152 ae->ac,
153 (const struct sockaddr *) &ae[1],
154 ae->addrlen);
155 GNUNET_free (ae);
156 }
138 nh->reconnect_delay 157 nh->reconnect_delay
139 = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay); 158 = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay);
140 nh->reconnect_task 159 nh->reconnect_task
@@ -260,6 +279,7 @@ handle_address_change_notification (void *cls,
260 if (GNUNET_YES == ntohl (acn->add_remove)) 279 if (GNUNET_YES == ntohl (acn->add_remove))
261 { 280 {
262 ae = GNUNET_malloc (sizeof (*ae) + alen); 281 ae = GNUNET_malloc (sizeof (*ae) + alen);
282 ae->ac = ac;
263 ae->addrlen = alen; 283 ae->addrlen = alen;
264 GNUNET_memcpy (&ae[1], 284 GNUNET_memcpy (&ae[1],
265 sa, 285 sa,
@@ -331,6 +351,7 @@ do_connect (void *cls)
331 nh), 351 nh),
332 GNUNET_MQ_handler_end () 352 GNUNET_MQ_handler_end ()
333 }; 353 };
354 struct GNUNET_MQ_Envelope *env;
334 355
335 nh->reconnect_task = NULL; 356 nh->reconnect_task = NULL;
336 nh->mq = GNUNET_CLIENT_connecT (nh->cfg, 357 nh->mq = GNUNET_CLIENT_connecT (nh->cfg,
@@ -339,7 +360,13 @@ do_connect (void *cls)
339 &mq_error_handler, 360 &mq_error_handler,
340 nh); 361 nh);
341 if (NULL == nh->mq) 362 if (NULL == nh->mq)
363 {
342 reconnect (nh); 364 reconnect (nh);
365 return;
366 }
367 env = GNUNET_MQ_msg_copy (nh->reg);
368 GNUNET_MQ_send (nh->mq,
369 env);
343} 370}
344 371
345 372
diff --git a/src/nat/nat_api_stun.c b/src/nat/nat_api_stun.c
index 8f5a5f8f6..7f2ef4eaf 100644
--- a/src/nat/nat_api_stun.c
+++ b/src/nat/nat_api_stun.c
@@ -199,9 +199,7 @@ stun_dns_callback (void *cls,
199 199
200/** 200/**
201 * Make Generic STUN request. Sends a generic stun request to the 201 * Make Generic STUN request. Sends a generic stun request to the
202 * server specified using the specified socket, possibly waiting for 202 * server specified using the specified socket.
203 * a reply and filling the 'reply' field with the externally visible
204 * address.
205 * 203 *
206 * @param server the address of the stun server 204 * @param server the address of the stun server
207 * @param port port of the stun server, in host byte order 205 * @param port port of the stun server, in host byte order
diff --git a/src/nat/nat_stun.h b/src/nat/nat_stun.h
index 4c6c178fb..3e4228875 100644
--- a/src/nat/nat_stun.h
+++ b/src/nat/nat_stun.h
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015 GNUnet e.V. 3 Copyright (C) 2009, 2015, 2016 GNUnet e.V.
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -135,23 +135,24 @@ enum StunAttributes {
135 * @param msg the received message 135 * @param msg the received message
136 * @return the converted StunClass 136 * @return the converted StunClass
137 */ 137 */
138static int 138static enum StunClasses
139decode_class(int msg) 139decode_class (int msg)
140{ 140{
141 /* Sorry for the magic, but this maps the class according to rfc5245 */ 141 /* Sorry for the magic, but this maps the class according to rfc5245 */
142 return ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); 142 return (enum StunClasses) ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7);
143} 143}
144 144
145
145/** 146/**
146 * Convert a message to a StunMethod 147 * Convert a message to a StunMethod
147 * 148 *
148 * @param msg the received message 149 * @param msg the received message
149 * @return the converted StunMethod 150 * @return the converted StunMethod
150 */ 151 */
151static int 152static enum StunMethods
152decode_method(int msg) 153decode_method (int msg)
153{ 154{
154 return (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2); 155 return (enum StunMethods) (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg & 0x3e00) >> 2);
155} 156}
156 157
157 158
@@ -161,6 +162,7 @@ decode_method(int msg)
161 * @param msg 162 * @param msg
162 * @return string with the message class and method 163 * @return string with the message class and method
163 */ 164 */
165GNUNET_UNUSED
164static const char * 166static const char *
165stun_msg2str (int msg) 167stun_msg2str (int msg)
166{ 168{
@@ -172,14 +174,14 @@ stun_msg2str (int msg)
172 { STUN_INDICATION, "Indication" }, 174 { STUN_INDICATION, "Indication" },
173 { STUN_RESPONSE, "Response" }, 175 { STUN_RESPONSE, "Response" },
174 { STUN_ERROR_RESPONSE, "Error Response" }, 176 { STUN_ERROR_RESPONSE, "Error Response" },
175 { 0, NULL } 177 { INVALID_CLASS, NULL }
176 }; 178 };
177 static const struct { 179 static const struct {
178 enum StunMethods value; 180 enum StunMethods value;
179 const char *name; 181 const char *name;
180 } methods[] = { 182 } methods[] = {
181 { STUN_BINDING, "Binding" }, 183 { STUN_BINDING, "Binding" },
182 { 0, NULL } 184 { INVALID_METHOD, NULL }
183 }; 185 };
184 static char result[64]; 186 static char result[64];
185 const char *msg_class = NULL; 187 const char *msg_class = NULL;
@@ -215,6 +217,7 @@ stun_msg2str (int msg)
215 * @param msg with a attribute type 217 * @param msg with a attribute type
216 * @return string with the attribute name 218 * @return string with the attribute name
217 */ 219 */
220GNUNET_UNUSED
218static const char * 221static const char *
219stun_attr2str (enum StunAttributes msg) 222stun_attr2str (enum StunAttributes msg)
220{ 223{
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?
51PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc 52PRIVATE_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)
62SYSTEM_TYPE = UNKNOWN
53 63
54[TESTING] 64[TESTING]
55SPEEDUP_INTERVAL = 0 ms 65SPEEDUP_INTERVAL = 0 ms