diff options
author | Christian Grothoff <christian@grothoff.org> | 2011-06-25 06:24:01 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2011-06-25 06:24:01 +0000 |
commit | 8a3016481ba6aeb36de3950a56e641dda53ca544 (patch) | |
tree | 0c5468ec6e5d2165ad5931940e0b8439d09398de /src/nat/nat.c | |
parent | b6d4eeb39fd0cd41df164bd0959f58b08aa45e83 (diff) | |
download | gnunet-8a3016481ba6aeb36de3950a56e641dda53ca544.tar.gz gnunet-8a3016481ba6aeb36de3950a56e641dda53ca544.zip |
the big NAT change
Diffstat (limited to 'src/nat/nat.c')
-rw-r--r-- | src/nat/nat.c | 1307 |
1 files changed, 1033 insertions, 274 deletions
diff --git a/src/nat/nat.c b/src/nat/nat.c index 7e83134bd..e92f57259 100644 --- a/src/nat/nat.c +++ b/src/nat/nat.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of GNUnet. | 2 | This file is part of GNUnet. |
3 | (C) 2009, 2010 Christian Grothoff (and other contributing authors) | 3 | (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) |
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 |
@@ -18,345 +18,841 @@ | |||
18 | Boston, MA 02111-1307, USA. | 18 | Boston, MA 02111-1307, USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /* | ||
22 | * Parts of this file have been adapted from the Transmission project: | ||
23 | * Originally licensed by the GPL version 2. | ||
24 | * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com> | ||
25 | */ | ||
26 | |||
27 | /** | 21 | /** |
28 | * @file nat/nat.c | 22 | * @file nat/nat.c |
29 | * @brief Library handling UPnP and NAT-PMP port forwarding and | 23 | * @brief Library handling UPnP and NAT-PMP port forwarding and |
30 | * external IP address retrieval | 24 | * external IP address retrieval |
31 | * | ||
32 | * @author Milan Bouchet-Valat | 25 | * @author Milan Bouchet-Valat |
26 | * @author Christian Grothoff | ||
27 | * | ||
28 | * TODO: | ||
29 | * - implement UPnP/PMP support | ||
30 | * - repeatedly perform certain checks again to notice changes | ||
33 | */ | 31 | */ |
34 | #include "platform.h" | 32 | #include "platform.h" |
35 | #include "gnunet_util_lib.h" | 33 | #include "gnunet_util_lib.h" |
34 | #include "gnunet_resolver_service.h" | ||
36 | #include "gnunet_nat_lib.h" | 35 | #include "gnunet_nat_lib.h" |
37 | #include "nat.h" | 36 | |
38 | #include "natpmp.h" | 37 | |
39 | #include "upnp.h" | 38 | /** |
39 | * How long until we give up on transmitting the welcome message? | ||
40 | */ | ||
41 | #define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
42 | |||
43 | |||
44 | /** | ||
45 | * Where did the given local address originate from? | ||
46 | * To be used for debugging as well as in the future | ||
47 | * to remove all addresses from a certain source when | ||
48 | * we reevaluate the source. | ||
49 | */ | ||
50 | enum LocalAddressSource | ||
51 | { | ||
52 | /** | ||
53 | * Address was obtained by DNS resolution of the external hostname | ||
54 | * given in the configuration (i.e. hole-punched DynDNS setup). | ||
55 | * FIXME: repeatedly do the lookup to notice changes! | ||
56 | */ | ||
57 | LAL_EXTERNAL_IP, | ||
58 | |||
59 | /** | ||
60 | * Address was obtained by looking up our own hostname in DNS. | ||
61 | * FIXME: repeatedly do the lookup to notice changes! | ||
62 | */ | ||
63 | LAL_HOSTNAME_DNS, | ||
64 | |||
65 | /** | ||
66 | * Address was obtained by scanning our hosts's network interfaces | ||
67 | * and taking their address (no DNS involved). | ||
68 | * FIXME: repeatedly do the lookup to notice changes! | ||
69 | */ | ||
70 | LAL_INTERFACE_ADDRESS, | ||
71 | |||
72 | /* TODO: add UPnP, etc. */ | ||
73 | |||
74 | /** | ||
75 | * End of the list. | ||
76 | */ | ||
77 | LAL_END | ||
78 | |||
79 | }; | ||
80 | |||
81 | |||
82 | /** | ||
83 | * List of local addresses that we currently deem valid. Actual | ||
84 | * struct is followed by the 'struct sockaddr'. Note that the code | ||
85 | * intentionally makes no attempt to ensure that a particular address | ||
86 | * is only listed once (especially since it may come from different | ||
87 | * sources, and the source is an "internal" construct). | ||
88 | */ | ||
89 | struct LocalAddressList | ||
90 | { | ||
91 | /** | ||
92 | * This is a linked list. | ||
93 | */ | ||
94 | struct LocalAddressList *next; | ||
95 | |||
96 | /** | ||
97 | * Previous entry. | ||
98 | */ | ||
99 | struct LocalAddressList *prev; | ||
100 | |||
101 | /** | ||
102 | * Number of bytes of address that follow. | ||
103 | */ | ||
104 | socklen_t addrlen; | ||
105 | |||
106 | /** | ||
107 | * Origin of the local address. | ||
108 | */ | ||
109 | enum LocalAddressSource source; | ||
110 | }; | ||
111 | |||
40 | 112 | ||
41 | /** | 113 | /** |
42 | * Handle for active NAT registrations. | 114 | * Handle for active NAT registrations. |
43 | */ | 115 | */ |
44 | struct GNUNET_NAT_Handle | 116 | struct GNUNET_NAT_Handle |
45 | { | 117 | { |
118 | |||
119 | /** | ||
120 | * Configuration to use. | ||
121 | */ | ||
122 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
123 | |||
124 | /** | ||
125 | * Function to call when we learn about a new address. | ||
126 | */ | ||
127 | GNUNET_NAT_AddressCallback address_callback; | ||
128 | |||
46 | /** | 129 | /** |
47 | * Handle for UPnP operations. | 130 | * Function to call when we notice another peer asking for |
131 | * connection reversal. | ||
48 | */ | 132 | */ |
49 | struct GNUNET_NAT_UPNP_Handle *upnp; | 133 | GNUNET_NAT_ReversalCallback reversal_callback; |
50 | 134 | ||
51 | /** | 135 | /** |
52 | * Handle for NAT PMP operations. | 136 | * Closure for 'callback'. |
53 | */ | 137 | */ |
54 | struct GNUNET_NAT_NATPMP_Handle *natpmp; | 138 | void *callback_cls; |
55 | 139 | ||
56 | /** | 140 | /** |
57 | * LAN address as passed by the caller | 141 | * Handle for (DYN)DNS lookup of our external IP. |
58 | */ | 142 | */ |
59 | struct sockaddr *local_addr; | 143 | struct GNUNET_RESOLVER_RequestHandle *ext_dns; |
60 | 144 | ||
61 | /** | 145 | /** |
62 | * External address as reported by found NAT box | 146 | * Handle for request of hostname resolution, non-NULL if pending. |
63 | */ | 147 | */ |
64 | struct sockaddr *ext_addr; | 148 | struct GNUNET_RESOLVER_RequestHandle *hostname_dns; |
65 | 149 | ||
66 | /** | 150 | /** |
67 | * External address as reported by each type of NAT box | 151 | * stdout pipe handle for the gnunet-nat-server process |
68 | */ | 152 | */ |
69 | struct sockaddr *ext_addr_upnp; | 153 | struct GNUNET_DISK_PipeHandle *server_stdout; |
70 | struct sockaddr *ext_addr_natpmp; | ||
71 | 154 | ||
72 | /** | 155 | /** |
73 | * External address and port where packets are redirected | 156 | * stdout file handle (for reading) for the gnunet-nat-server process |
74 | */ | 157 | */ |
75 | struct sockaddr *contact_addr; | 158 | const struct GNUNET_DISK_FileHandle *server_stdout_handle; |
76 | 159 | ||
77 | GNUNET_NAT_AddressCallback callback; | 160 | /** |
161 | * Linked list of currently valid addresses (head). | ||
162 | */ | ||
163 | struct LocalAddressList *lal_head; | ||
78 | 164 | ||
79 | /** | 165 | /** |
80 | * Closure for 'callback'. | 166 | * Linked list of currently valid addresses (tail). |
81 | */ | 167 | */ |
82 | void *callback_cls; | 168 | struct LocalAddressList *lal_tail; |
83 | 169 | ||
84 | GNUNET_SCHEDULER_TaskIdentifier pulse_timer; | 170 | /** |
171 | * How long do we wait for restarting a crashed gnunet-nat-server? | ||
172 | */ | ||
173 | struct GNUNET_TIME_Relative server_retry_delay; | ||
85 | 174 | ||
86 | enum GNUNET_NAT_PortState natpmp_status; | 175 | /** |
176 | * ID of select gnunet-nat-server stdout read task | ||
177 | */ | ||
178 | GNUNET_SCHEDULER_TaskIdentifier server_read_task; | ||
87 | 179 | ||
88 | enum GNUNET_NAT_PortState upnp_status; | 180 | /** |
181 | * The process id of the server process (if behind NAT) | ||
182 | */ | ||
183 | struct GNUNET_OS_Process *server_proc; | ||
89 | 184 | ||
90 | int is_enabled; | 185 | /** |
186 | * LAN address as passed by the caller (array). | ||
187 | */ | ||
188 | struct sockaddr **local_addrs; | ||
91 | 189 | ||
92 | int should_change; | 190 | /** |
191 | * Length of the 'local_addrs'. | ||
192 | */ | ||
193 | socklen_t *local_addrlens; | ||
194 | |||
195 | /** | ||
196 | * Number of entries in 'local_addrs' array. | ||
197 | */ | ||
198 | unsigned int num_local_addrs; | ||
199 | |||
200 | /** | ||
201 | * The our external address (according to config, UPnP may disagree...) | ||
202 | */ | ||
203 | char *external_address; | ||
204 | |||
205 | /** | ||
206 | * Presumably our internal address (according to config) | ||
207 | */ | ||
208 | char *internal_address; | ||
209 | |||
210 | /** | ||
211 | * Is this transport configured to be behind a NAT? | ||
212 | */ | ||
213 | int behind_nat; | ||
214 | |||
215 | /** | ||
216 | * Has the NAT been punched? (according to config) | ||
217 | */ | ||
218 | int nat_punched; | ||
219 | |||
220 | /** | ||
221 | * Is this transport configured to allow connections to NAT'd peers? | ||
222 | */ | ||
223 | int enable_nat_client; | ||
93 | 224 | ||
94 | int port_mapped; | 225 | /** |
226 | * Should we run the gnunet-nat-server? | ||
227 | */ | ||
228 | int enable_nat_server; | ||
95 | 229 | ||
96 | int old_status; | 230 | /** |
231 | * Are we allowed to try UPnP/PMP for NAT traversal? | ||
232 | */ | ||
233 | int enable_upnp; | ||
97 | 234 | ||
98 | int new_status; | 235 | /** |
236 | * Should we use local addresses (loopback)? (according to config) | ||
237 | */ | ||
238 | int use_localaddresses; | ||
99 | 239 | ||
100 | int did_warn; | 240 | /** |
241 | * Is using IPv6 disabled? | ||
242 | */ | ||
243 | int disable_ipv6; | ||
101 | 244 | ||
102 | int processing; | 245 | /** |
246 | * Is this TCP or UDP? | ||
247 | */ | ||
248 | int is_tcp; | ||
103 | 249 | ||
104 | uint16_t public_port; | 250 | /** |
251 | * Port we advertise to the outside. | ||
252 | */ | ||
253 | uint16_t adv_port; | ||
105 | 254 | ||
106 | }; | 255 | }; |
107 | 256 | ||
108 | #ifdef DEBUG | ||
109 | static const char * | ||
110 | get_nat_state_str (enum GNUNET_NAT_PortState state) | ||
111 | { | ||
112 | switch (state) | ||
113 | { | ||
114 | case GNUNET_NAT_PORT_MAPPING: | ||
115 | return "Starting"; | ||
116 | case GNUNET_NAT_PORT_MAPPED: | ||
117 | return "Forwarded"; | ||
118 | case GNUNET_NAT_PORT_UNMAPPING: | ||
119 | return "Stopping"; | ||
120 | case GNUNET_NAT_PORT_UNMAPPED: | ||
121 | return "Not forwarded"; | ||
122 | case GNUNET_NAT_PORT_ERROR: | ||
123 | return "Redirection failed"; | ||
124 | default: | ||
125 | return "not found"; | ||
126 | } | ||
127 | } | ||
128 | #endif | ||
129 | 257 | ||
258 | /** | ||
259 | * Try to start the gnunet-nat-server (if it is not | ||
260 | * already running). | ||
261 | * | ||
262 | * @param h handle to NAT | ||
263 | */ | ||
264 | static void | ||
265 | start_gnunet_nat_server (struct GNUNET_NAT_Handle *h); | ||
130 | 266 | ||
131 | static int | 267 | |
132 | get_traversal_status (const struct GNUNET_NAT_Handle *h) | 268 | /** |
269 | * Add the given address to the list of 'local' addresses, thereby | ||
270 | * making it a 'legal' address for this peer to have. | ||
271 | * | ||
272 | * @param plugin the plugin | ||
273 | * @param src where did the local address originate from? | ||
274 | * @param arg the address, some 'struct sockaddr' | ||
275 | * @param arg_size number of bytes in arg | ||
276 | */ | ||
277 | static void | ||
278 | add_to_address_list_as_is (struct GNUNET_NAT_Handle *h, | ||
279 | enum LocalAddressSource src, | ||
280 | const struct sockaddr *arg, | ||
281 | socklen_t arg_size) | ||
133 | { | 282 | { |
134 | return GNUNET_MAX (h->natpmp_status, h->upnp_status); | 283 | struct LocalAddressList *lal; |
284 | |||
285 | lal = GNUNET_malloc (sizeof (struct LocalAddressList) + arg_size); | ||
286 | memcpy (&lal[1], arg, arg_size); | ||
287 | lal->addrlen = arg_size; | ||
288 | lal->source = src; | ||
289 | GNUNET_CONTAINER_DLL_insert (h->lal_head, | ||
290 | h->lal_tail, | ||
291 | lal); | ||
292 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
293 | "nat", | ||
294 | "Adding address `%s' from source %d\n", | ||
295 | GNUNET_a2s (arg, arg_size), | ||
296 | src); | ||
297 | h->address_callback (h->callback_cls, | ||
298 | GNUNET_YES, | ||
299 | arg, | ||
300 | arg_size); | ||
135 | } | 301 | } |
136 | 302 | ||
137 | 303 | ||
138 | /** | 304 | /** |
139 | * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr. | 305 | * Add the given address to the list of 'local' addresses, thereby |
140 | * @param a first sockaddr | 306 | * making it a 'legal' address for this peer to have. Set the |
141 | * @param b second sockaddr | 307 | * port number in the process to the advertised port and possibly |
142 | * @return 0 if addresses are equal, non-null value otherwise */ | 308 | * also to zero (if we have the gnunet-nat-server). |
143 | int | 309 | * |
144 | GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b) | 310 | * @param plugin the plugin |
311 | * @param src where did the local address originate from? | ||
312 | * @param arg the address, some 'struct sockaddr' | ||
313 | * @param arg_size number of bytes in arg | ||
314 | */ | ||
315 | static void | ||
316 | add_to_address_list (struct GNUNET_NAT_Handle *h, | ||
317 | enum LocalAddressSource src, | ||
318 | const struct sockaddr *arg, | ||
319 | socklen_t arg_size) | ||
145 | { | 320 | { |
146 | if (!(a && b)) | 321 | struct sockaddr_in s4; |
147 | return -1; | 322 | const struct sockaddr_in *in4; |
148 | if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET)) | 323 | struct sockaddr_in6 s6; |
149 | return memcmp (&(((struct sockaddr_in *) a)->sin_addr), | 324 | const struct sockaddr_in6 *in6; |
150 | &(((struct sockaddr_in *) b)->sin_addr), | 325 | |
151 | sizeof (struct in_addr)); | 326 | if (arg_size == sizeof (struct sockaddr_in)) |
152 | if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6)) | 327 | { |
153 | return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr), | 328 | in4 = (const struct sockaddr_in *) arg; |
154 | &(((struct sockaddr_in6 *) b)->sin6_addr), | 329 | s4 = *in4; |
155 | sizeof (struct in6_addr)); | 330 | s4.sin_port = htons (h->adv_port); |
156 | return -1; | 331 | add_to_address_list_as_is (h, |
332 | src, | ||
333 | (const struct sockaddr*) &s4, | ||
334 | sizeof (struct sockaddr_in)); | ||
335 | if (GNUNET_YES == h->enable_nat_server) | ||
336 | { | ||
337 | /* also add with PORT = 0 to indicate NAT server is enabled */ | ||
338 | s4.sin_port = htons(0); | ||
339 | add_to_address_list_as_is (h, | ||
340 | src, | ||
341 | (const struct sockaddr*) &s4, | ||
342 | sizeof (struct sockaddr_in)); | ||
343 | } | ||
344 | } | ||
345 | else if (arg_size == sizeof (struct sockaddr_in6)) | ||
346 | { | ||
347 | if (GNUNET_YES != h->disable_ipv6) | ||
348 | { | ||
349 | in6 = (const struct sockaddr_in6 *) arg; | ||
350 | s6 = *in6; | ||
351 | s6.sin6_port = htons(h->adv_port); | ||
352 | add_to_address_list_as_is (h, | ||
353 | src, | ||
354 | (const struct sockaddr*) &s6, | ||
355 | sizeof (struct sockaddr_in6)); | ||
356 | } | ||
357 | } | ||
358 | else | ||
359 | { | ||
360 | GNUNET_assert (0); | ||
361 | } | ||
157 | } | 362 | } |
158 | 363 | ||
159 | 364 | ||
160 | /** | 365 | /** |
161 | * Deal with a new IP address or port redirection: | 366 | * Add the given IP address to the list of 'local' addresses, thereby |
162 | * Send signals with the appropriate sockaddr (IP and port), free and changes | 367 | * making it a 'legal' address for this peer to have. |
163 | * or nullify the previous sockaddr. Change the port if needed. | 368 | * |
369 | * @param plugin the plugin | ||
370 | * @param src where did the local address originate from? | ||
371 | * @param arg the address, some 'struct in_addr' or 'struct in6_addr' | ||
372 | * @param arg_size number of bytes in arg | ||
164 | */ | 373 | */ |
165 | static void | 374 | static void |
166 | notify_change (struct GNUNET_NAT_Handle *h, | 375 | add_ip_to_address_list (struct GNUNET_NAT_Handle *h, |
167 | struct sockaddr *addr, size_t addrlen, int new_port_mapped) | 376 | enum LocalAddressSource src, |
377 | const void *addr, | ||
378 | socklen_t addrlen) | ||
168 | { | 379 | { |
169 | if (new_port_mapped == h->port_mapped) | 380 | struct sockaddr_in s4; |
170 | return; | 381 | const struct in_addr *in4; |
171 | h->port_mapped = new_port_mapped; | 382 | struct sockaddr_in6 s6; |
172 | 383 | const struct in6_addr *in6; | |
173 | if ((NULL != h->contact_addr) && (NULL != h->callback)) | ||
174 | h->callback (h->callback_cls, | ||
175 | GNUNET_NO, h->contact_addr, sizeof (h->contact_addr)); | ||
176 | GNUNET_free_non_null (h->contact_addr); | ||
177 | h->contact_addr = NULL; | ||
178 | GNUNET_free_non_null (h->ext_addr); | ||
179 | h->ext_addr = NULL; | ||
180 | if (NULL == addr) | ||
181 | return; | ||
182 | h->ext_addr = GNUNET_malloc (addrlen); | ||
183 | memcpy (h->ext_addr, addr, addrlen); | ||
184 | 384 | ||
185 | /* Recreate the ext_addr:public_port bogus address to pass to the callback */ | 385 | if (addrlen == sizeof (struct in_addr)) |
186 | if (h->ext_addr->sa_family == AF_INET) | ||
187 | { | 386 | { |
188 | struct sockaddr_in *tmp_addr; | 387 | in4 = (const struct in_addr *) addr; |
189 | 388 | memset (&s4, 0, sizeof (s4)); | |
190 | tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); | 389 | s4.sin_family = AF_INET; |
191 | tmp_addr->sin_family = AF_INET; | 390 | s4.sin_port = 0; |
192 | #ifdef HAVE_SOCKADDR_IN_SIN_LEN | 391 | #if HAVE_SOCKADDR_IN_SIN_LEN |
193 | tmp_addr->sin_len = sizeof (struct sockaddr_in); | 392 | s4.sin_len = (u_char) sizeof (struct sockaddr_in); |
194 | #endif | 393 | #endif |
195 | tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0; | 394 | s4.sin_addr = *in4; |
196 | tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr; | 395 | add_to_address_list (h, |
197 | h->contact_addr = (struct sockaddr *) tmp_addr; | 396 | src, |
198 | 397 | (const struct sockaddr*) &s4, | |
199 | if (NULL != h->callback) | 398 | sizeof (struct sockaddr_in)); |
200 | h->callback (h->callback_cls, | 399 | if (GNUNET_YES == h->enable_nat_server) |
201 | GNUNET_YES, | 400 | { |
202 | h->contact_addr, sizeof (struct sockaddr_in)); | 401 | /* also add with PORT = 0 to indicate NAT server is enabled */ |
402 | s4.sin_port = htons(0); | ||
403 | add_to_address_list (h, | ||
404 | src, | ||
405 | (const struct sockaddr*) &s4, | ||
406 | sizeof (struct sockaddr_in)); | ||
407 | |||
408 | } | ||
203 | } | 409 | } |
204 | else if (h->ext_addr->sa_family == AF_INET6) | 410 | else if (addrlen == sizeof (struct in6_addr)) |
205 | { | 411 | { |
206 | struct sockaddr_in6 *tmp_addr; | 412 | if (GNUNET_YES != h->disable_ipv6) |
207 | 413 | { | |
208 | tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); | 414 | in6 = (const struct in6_addr *) addr; |
209 | tmp_addr->sin6_family = AF_INET6; | 415 | memset (&s6, 0, sizeof (s6)); |
210 | #ifdef HAVE_SOCKADDR_IN_SIN_LEN | 416 | s6.sin6_family = AF_INET6; |
211 | tmp_addr->sin6_len = sizeof (struct sockaddr_in6); | 417 | s6.sin6_port = htons(h->adv_port); |
418 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
419 | s6.sin6_len = (u_char) sizeof (struct sockaddr_in6); | ||
212 | #endif | 420 | #endif |
213 | tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0; | 421 | s6.sin6_addr = *in6; |
214 | tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr; | 422 | add_to_address_list (h, |
215 | h->contact_addr = (struct sockaddr *) tmp_addr; | 423 | src, |
216 | 424 | (const struct sockaddr*) &s6, | |
217 | if (NULL != h->callback) | 425 | sizeof (struct sockaddr_in6)); |
218 | h->callback (h->callback_cls, | 426 | } |
219 | GNUNET_YES, | ||
220 | h->contact_addr, sizeof (struct sockaddr_in6)); | ||
221 | } | 427 | } |
222 | else | 428 | else |
223 | { | 429 | { |
224 | GNUNET_break (0); | 430 | GNUNET_assert (0); |
225 | } | 431 | } |
226 | } | 432 | } |
227 | 433 | ||
228 | static void nat_pulse (void *cls, | ||
229 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
230 | 434 | ||
435 | /** | ||
436 | * Our (external) hostname was resolved and the configuration says that | ||
437 | * the NAT was hole-punched. | ||
438 | * | ||
439 | * @param cls the 'struct Plugin' | ||
440 | * @param addr NULL on error, otherwise result of DNS lookup | ||
441 | * @param addrlen number of bytes in addr | ||
442 | */ | ||
231 | static void | 443 | static void |
232 | pulse_cb (struct GNUNET_NAT_Handle *h) | 444 | process_external_ip (void *cls, |
445 | const struct sockaddr *addr, | ||
446 | socklen_t addrlen) | ||
233 | { | 447 | { |
234 | socklen_t addrlen; | 448 | struct GNUNET_NAT_Handle *h = cls; |
235 | int port_mapped; | ||
236 | 449 | ||
237 | /* One of the protocols is still working, wait for it to complete */ | 450 | if (addr == NULL) |
238 | if (h->processing) | 451 | { |
239 | return; | 452 | h->ext_dns = NULL; |
453 | /* FIXME: schedule task to resolve IP again in the | ||
454 | future, and if the result changes, update the | ||
455 | local address list accordingly */ | ||
456 | return; | ||
457 | } | ||
458 | add_to_address_list (h, LAL_EXTERNAL_IP, addr, addrlen); | ||
459 | } | ||
240 | 460 | ||
241 | h->new_status = get_traversal_status (h); | ||
242 | if ((h->old_status != h->new_status) && | ||
243 | ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) || | ||
244 | (h->new_status == GNUNET_NAT_PORT_ERROR))) | ||
245 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, | ||
246 | "NAT", | ||
247 | _ | ||
248 | ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n")); | ||
249 | #ifdef DEBUG | ||
250 | if (h->new_status != h->old_status) | ||
251 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT", | ||
252 | _("State changed from `%s' to `%s'\n"), | ||
253 | get_nat_state_str (h->old_status), | ||
254 | get_nat_state_str (h->new_status)); | ||
255 | #endif | ||
256 | 461 | ||
257 | port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED); | 462 | /** |
258 | if (!(h->ext_addr_upnp || h->ext_addr_natpmp)) | 463 | * Function called by the resolver for each address obtained from DNS |
464 | * for our own hostname. Add the addresses to the list of our IP | ||
465 | * addresses. | ||
466 | * | ||
467 | * @param cls closure | ||
468 | * @param addr one of the addresses of the host, NULL for the last address | ||
469 | * @param addrlen length of the address | ||
470 | */ | ||
471 | static void | ||
472 | process_hostname_ip (void *cls, | ||
473 | const struct sockaddr *addr, socklen_t addrlen) | ||
474 | { | ||
475 | struct GNUNET_NAT_Handle *h = cls; | ||
476 | |||
477 | if (addr == NULL) | ||
259 | { | 478 | { |
260 | /* Address has just changed and we could not get it, or it's the first try, | 479 | h->hostname_dns = NULL; |
261 | * and we're not waiting for a reply from UPnP or NAT-PMP */ | 480 | /* FIXME: schedule task to resolve IP again in the |
262 | if (((NULL != h->ext_addr) || | 481 | future, and if the result changes, update the |
263 | (GNUNET_NO == h->did_warn)) && h->processing != 0) | 482 | address list accordingly */ |
264 | { | 483 | return; |
265 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, | ||
266 | "NAT", | ||
267 | _("Could not determine external IP address\n")); | ||
268 | h->did_warn = GNUNET_YES; | ||
269 | } | ||
270 | notify_change (h, NULL, 0, port_mapped); | ||
271 | } | 484 | } |
272 | else if (h->ext_addr_upnp | 485 | add_to_address_list (h, LAL_HOSTNAME_DNS, addr, addrlen); |
273 | && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0) | 486 | } |
487 | |||
488 | |||
489 | /** | ||
490 | * Add the IP of our network interface to the list of | ||
491 | * our IP addresses. | ||
492 | * | ||
493 | * @param cls the 'struct GNUNET_NAT_Handle' | ||
494 | * @param name name of the interface | ||
495 | * @param isDefault do we think this may be our default interface | ||
496 | * @param addr address of the interface | ||
497 | * @param addrlen number of bytes in addr | ||
498 | * @return GNUNET_OK to continue iterating | ||
499 | */ | ||
500 | static int | ||
501 | process_interfaces (void *cls, | ||
502 | const char *name, | ||
503 | int isDefault, | ||
504 | const struct sockaddr *addr, socklen_t addrlen) | ||
505 | { | ||
506 | struct GNUNET_NAT_Handle *h = cls; | ||
507 | const struct sockaddr_in *s4; | ||
508 | const struct sockaddr_in6 *s6; | ||
509 | const void *ip; | ||
510 | char buf[INET6_ADDRSTRLEN]; | ||
511 | |||
512 | switch (addr->sa_family) | ||
274 | { | 513 | { |
275 | addrlen = h->ext_addr_upnp->sa_family == AF_INET ? | 514 | case AF_INET: |
276 | sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); | 515 | s4 = (struct sockaddr_in *) addr; |
277 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, | 516 | ip = &s4->sin_addr; |
278 | "NAT", | 517 | if (GNUNET_YES == h->use_localaddresses) |
279 | _("External IP address changed to %s\n"), | 518 | add_ip_to_address_list (h, |
280 | GNUNET_a2s (h->ext_addr_upnp, addrlen)); | 519 | LAL_INTERFACE_ADDRESS, |
281 | notify_change (h, h->ext_addr_upnp, addrlen, port_mapped); | 520 | &s4->sin_addr, |
521 | sizeof (struct in_addr)); | ||
522 | break; | ||
523 | case AF_INET6: | ||
524 | s6 = (struct sockaddr_in6 *) addr; | ||
525 | if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) | ||
526 | { | ||
527 | /* skip link local addresses */ | ||
528 | return GNUNET_OK; | ||
529 | } | ||
530 | ip = &s6->sin6_addr; | ||
531 | if (GNUNET_YES == h->use_localaddresses) | ||
532 | add_ip_to_address_list (h, | ||
533 | LAL_INTERFACE_ADDRESS, | ||
534 | &s6->sin6_addr, | ||
535 | sizeof (struct in6_addr)); | ||
536 | break; | ||
537 | default: | ||
538 | GNUNET_break (0); | ||
539 | break; | ||
282 | } | 540 | } |
283 | else if (h->ext_addr_natpmp | 541 | if ( (h->internal_address == NULL) && |
284 | && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0) | 542 | (h->server_proc == NULL) && |
543 | (h->server_read_task == GNUNET_SCHEDULER_NO_TASK) && | ||
544 | (GNUNET_YES == isDefault) && | ||
545 | ( (addr->sa_family == AF_INET) || (addr->sa_family == AF_INET6) ) ) | ||
285 | { | 546 | { |
286 | addrlen = h->ext_addr_natpmp->sa_family == AF_INET ? | 547 | /* no internal address configured, but we found a "default" |
287 | sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); | 548 | interface, try using that as our 'internal' address */ |
288 | GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT", | 549 | h->internal_address = GNUNET_strdup (inet_ntop (addr->sa_family, |
289 | _("External IP address changed to `%s'\n"), | 550 | ip, |
290 | GNUNET_a2s (h->ext_addr_natpmp, addrlen)); | 551 | buf, |
291 | notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped); | 552 | sizeof (buf))); |
553 | start_gnunet_nat_server (h); | ||
292 | } | 554 | } |
293 | 555 | return GNUNET_OK; | |
294 | h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
295 | &nat_pulse, h); | ||
296 | } | 556 | } |
297 | 557 | ||
298 | static void | 558 | |
299 | upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls) | 559 | /** |
560 | * Return the actual path to a file found in the current | ||
561 | * PATH environment variable. | ||
562 | * | ||
563 | * @param binary the name of the file to find | ||
564 | * @return path to binary, NULL if not found | ||
565 | */ | ||
566 | static char * | ||
567 | get_path_from_PATH (const char *binary) | ||
300 | { | 568 | { |
301 | struct GNUNET_NAT_Handle *h = cls; | 569 | char *path; |
570 | char *pos; | ||
571 | char *end; | ||
572 | char *buf; | ||
573 | const char *p; | ||
574 | |||
575 | p = getenv ("PATH"); | ||
576 | if (p == NULL) | ||
577 | { | ||
578 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
579 | "tcp", | ||
580 | _("PATH environment variable is unset.\n")); | ||
581 | return NULL; | ||
582 | } | ||
583 | path = GNUNET_strdup (p); /* because we write on it */ | ||
584 | buf = GNUNET_malloc (strlen (path) + 20); | ||
585 | pos = path; | ||
586 | |||
587 | while (NULL != (end = strchr (pos, PATH_SEPARATOR))) | ||
588 | { | ||
589 | *end = '\0'; | ||
590 | sprintf (buf, "%s/%s", pos, binary); | ||
591 | if (GNUNET_DISK_file_test (buf) == GNUNET_YES) | ||
592 | { | ||
593 | GNUNET_free (path); | ||
594 | return buf; | ||
595 | } | ||
596 | pos = end + 1; | ||
597 | } | ||
598 | sprintf (buf, "%s/%s", pos, binary); | ||
599 | if (GNUNET_DISK_file_test (buf) == GNUNET_YES) | ||
600 | { | ||
601 | GNUNET_free (path); | ||
602 | return buf; | ||
603 | } | ||
604 | GNUNET_free (buf); | ||
605 | GNUNET_free (path); | ||
606 | return NULL; | ||
607 | } | ||
302 | 608 | ||
303 | h->upnp_status = status; | ||
304 | h->ext_addr_upnp = ext_addr; | ||
305 | 609 | ||
306 | h->processing--; | 610 | /** |
307 | pulse_cb (h); | 611 | * Check whether the suid bit is set on a file. |
612 | * Attempts to find the file using the current | ||
613 | * PATH environment variable as a search path. | ||
614 | * | ||
615 | * @param binary the name of the file to check | ||
616 | * @return GNUNET_YES if the file is SUID, | ||
617 | * GNUNET_NO if not, | ||
618 | * GNUNET_SYSERR on error | ||
619 | */ | ||
620 | static int | ||
621 | check_gnunet_nat_binary (const char *binary) | ||
622 | { | ||
623 | struct stat statbuf; | ||
624 | char *p; | ||
625 | #ifdef MINGW | ||
626 | SOCKET rawsock; | ||
627 | char *binaryexe; | ||
628 | |||
629 | GNUNET_asprintf (&binaryexe, "%s.exe", binary); | ||
630 | p = get_path_from_PATH (binaryexe); | ||
631 | free (binaryexe); | ||
632 | #else | ||
633 | p = get_path_from_PATH (binary); | ||
634 | #endif | ||
635 | if (p == NULL) | ||
636 | { | ||
637 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
638 | "tcp", | ||
639 | _("Could not find binary `%s' in PATH!\n"), | ||
640 | binary); | ||
641 | return GNUNET_NO; | ||
642 | } | ||
643 | if (0 != STAT (p, &statbuf)) | ||
644 | { | ||
645 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
646 | _("stat (%s) failed: %s\n"), | ||
647 | p, | ||
648 | STRERROR (errno)); | ||
649 | GNUNET_free (p); | ||
650 | return GNUNET_SYSERR; | ||
651 | } | ||
652 | GNUNET_free (p); | ||
653 | #ifndef MINGW | ||
654 | if ( (0 != (statbuf.st_mode & S_ISUID)) && | ||
655 | (statbuf.st_uid == 0) ) | ||
656 | return GNUNET_YES; | ||
657 | return GNUNET_NO; | ||
658 | #else | ||
659 | rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); | ||
660 | if (INVALID_SOCKET == rawsock) | ||
661 | { | ||
662 | DWORD err = GetLastError (); | ||
663 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
664 | "tcp", | ||
665 | "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err); | ||
666 | return GNUNET_NO; /* not running as administrator */ | ||
667 | } | ||
668 | closesocket (rawsock); | ||
669 | return GNUNET_YES; | ||
670 | #endif | ||
308 | } | 671 | } |
309 | 672 | ||
310 | #if 0 | 673 | |
674 | /** | ||
675 | * Task that restarts the gnunet-nat-server process after a crash | ||
676 | * after a certain delay. | ||
677 | * | ||
678 | * @param cls the 'struct GNUNET_NAT_Handle' | ||
679 | * @param tc scheduler context | ||
680 | */ | ||
311 | static void | 681 | static void |
312 | natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls) | 682 | restart_nat_server (void *cls, |
683 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
313 | { | 684 | { |
314 | struct GNUNET_NAT_Handle *h = cls; | 685 | struct GNUNET_NAT_Handle *h = cls; |
315 | 686 | ||
316 | h->natpmp_status = status; | 687 | h->server_read_task = GNUNET_SCHEDULER_NO_TASK; |
317 | h->ext_addr_natpmp = ext_addr; | 688 | if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) |
318 | 689 | return; | |
319 | h->processing--; | 690 | start_gnunet_nat_server (h); |
320 | pulse_cb (h); | ||
321 | } | 691 | } |
322 | #endif | ||
323 | 692 | ||
693 | |||
694 | /** | ||
695 | * We have been notified that gnunet-nat-server has written something to stdout. | ||
696 | * Handle the output, then reschedule this function to be called again once | ||
697 | * more is available. | ||
698 | * | ||
699 | * @param cls the NAT handle | ||
700 | * @param tc the scheduling context | ||
701 | */ | ||
324 | static void | 702 | static void |
325 | nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 703 | nat_server_read (void *cls, |
704 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
326 | { | 705 | { |
327 | struct GNUNET_NAT_Handle *h = cls; | 706 | struct GNUNET_NAT_Handle *h = cls; |
328 | 707 | char mybuf[40]; | |
329 | /* Stop if we're already waiting for an action to complete */ | 708 | ssize_t bytes; |
330 | h->pulse_timer = GNUNET_SCHEDULER_NO_TASK; | 709 | size_t i; |
331 | if (h->processing) | 710 | int port; |
711 | const char *port_start; | ||
712 | struct sockaddr_in sin_addr; | ||
713 | |||
714 | h->server_read_task = GNUNET_SCHEDULER_NO_TASK; | ||
715 | if ( (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) | ||
332 | return; | 716 | return; |
333 | h->old_status = get_traversal_status (h); | 717 | memset (mybuf, 0, sizeof(mybuf)); |
334 | 718 | bytes = GNUNET_DISK_file_read(h->server_stdout_handle, | |
335 | /* Only update the protocol that has been successful until now */ | 719 | mybuf, |
336 | if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED) | 720 | sizeof(mybuf)); |
721 | if (bytes < 1) | ||
337 | { | 722 | { |
338 | h->processing = 1; | 723 | #if DEBUG_TCP_NAT |
339 | GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES); | 724 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, |
340 | 725 | "nat", | |
341 | /* Wait for the callback to call pulse_cb() to handle changes */ | 726 | "Finished reading from server stdout with code: %d\n", |
727 | bytes); | ||
728 | #endif | ||
729 | if (0 != GNUNET_OS_process_kill (h->server_proc, SIGTERM)) | ||
730 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
731 | GNUNET_OS_process_wait (h->server_proc); | ||
732 | GNUNET_OS_process_close (h->server_proc); | ||
733 | h->server_proc = NULL; | ||
734 | GNUNET_DISK_pipe_close (h->server_stdout); | ||
735 | h->server_stdout = NULL; | ||
736 | h->server_stdout_handle = NULL; | ||
737 | /* now try to restart it */ | ||
738 | h->server_retry_delay = GNUNET_TIME_relative_multiply (h->server_retry_delay, 2); | ||
739 | h->server_retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS, | ||
740 | h->server_retry_delay); | ||
741 | h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay, | ||
742 | &restart_nat_server, | ||
743 | h); | ||
342 | return; | 744 | return; |
343 | } | 745 | } |
344 | else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED) | 746 | |
747 | port_start = NULL; | ||
748 | for (i = 0; i < sizeof(mybuf); i++) | ||
345 | { | 749 | { |
346 | h->processing = 1; | 750 | if (mybuf[i] == '\n') |
347 | #if 0 | 751 | { |
348 | GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled); | 752 | mybuf[i] = '\0'; |
349 | #endif | 753 | break; |
754 | } | ||
755 | if ( (mybuf[i] == ':') && (i + 1 < sizeof(mybuf)) ) | ||
756 | { | ||
757 | mybuf[i] = '\0'; | ||
758 | port_start = &mybuf[i + 1]; | ||
759 | } | ||
350 | } | 760 | } |
351 | else /* try both */ | ||
352 | { | ||
353 | h->processing = 2; | ||
354 | 761 | ||
355 | GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES); | 762 | /* construct socket address of sender */ |
356 | #if 0 | 763 | memset (&sin_addr, 0, sizeof (sin_addr)); |
357 | GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, &natpmp_pulse_cb, h); | 764 | sin_addr.sin_family = AF_INET; |
765 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
766 | sin_addr.sin_len = sizeof (sin_addr); | ||
358 | #endif | 767 | #endif |
768 | if ( (NULL == port_start) || | ||
769 | (1 != sscanf (port_start, "%d", &port)) || | ||
770 | (-1 == inet_pton(AF_INET, mybuf, &sin_addr.sin_addr)) ) | ||
771 | { | ||
772 | /* should we restart gnunet-nat-server? */ | ||
773 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
774 | "nat", | ||
775 | _("gnunet-nat-server generated malformed address `%s'\n"), | ||
776 | mybuf); | ||
777 | h->server_read_task | ||
778 | = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
779 | h->server_stdout_handle, | ||
780 | &nat_server_read, | ||
781 | h); | ||
782 | return; | ||
359 | } | 783 | } |
784 | sin_addr.sin_port = htons((uint16_t) port); | ||
785 | #if DEBUG_NAT | ||
786 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
787 | "nat", | ||
788 | "gnunet-nat-server read: %s:%d\n", | ||
789 | mybuf, port); | ||
790 | #endif | ||
791 | h->reversal_callback (h->callback_cls, | ||
792 | (const struct sockaddr*) &sin_addr, | ||
793 | sizeof (sin_addr)); | ||
794 | h->server_read_task = | ||
795 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
796 | h->server_stdout_handle, | ||
797 | &nat_server_read, | ||
798 | h); | ||
799 | } | ||
800 | |||
801 | |||
802 | /** | ||
803 | * Try to start the gnunet-nat-server (if it is not | ||
804 | * already running). | ||
805 | * | ||
806 | * @param h handle to NAT | ||
807 | */ | ||
808 | static void | ||
809 | start_gnunet_nat_server (struct GNUNET_NAT_Handle *h) | ||
810 | { | ||
811 | if ( (h->behind_nat == GNUNET_YES) && | ||
812 | (h->enable_nat_server == GNUNET_YES) && | ||
813 | (h->internal_address != NULL) && | ||
814 | (NULL != (h->server_stdout = GNUNET_DISK_pipe (GNUNET_YES, | ||
815 | GNUNET_NO, | ||
816 | GNUNET_YES))) ) | ||
817 | { | ||
818 | #if DEBUG_NAT | ||
819 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
820 | "nat" | ||
821 | "Starting %s at `%s'\n", | ||
822 | "gnunet-nat-server", | ||
823 | h->internal_address); | ||
824 | #endif | ||
825 | /* Start the server process */ | ||
826 | h->server_proc = GNUNET_OS_start_process (NULL, | ||
827 | h->server_stdout, | ||
828 | "gnunet-nat-server", | ||
829 | "gnunet-nat-server", | ||
830 | h->internal_address, | ||
831 | NULL); | ||
832 | if (h->server_proc == NULL) | ||
833 | { | ||
834 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
835 | "nat", | ||
836 | _("Failed to start %s\n"), | ||
837 | "gnunet-nat-server"); | ||
838 | GNUNET_DISK_pipe_close (h->server_stdout); | ||
839 | h->server_stdout = NULL; | ||
840 | } | ||
841 | else | ||
842 | { | ||
843 | /* Close the write end of the read pipe */ | ||
844 | GNUNET_DISK_pipe_close_end(h->server_stdout, | ||
845 | GNUNET_DISK_PIPE_END_WRITE); | ||
846 | h->server_stdout_handle | ||
847 | = GNUNET_DISK_pipe_handle (h->server_stdout, | ||
848 | GNUNET_DISK_PIPE_END_READ); | ||
849 | h->server_read_task | ||
850 | = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
851 | h->server_stdout_handle, | ||
852 | &nat_server_read, | ||
853 | h); | ||
854 | } | ||
855 | } | ||
360 | } | 856 | } |
361 | 857 | ||
362 | 858 | ||
@@ -367,55 +863,165 @@ nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
367 | * is taken from the corresponding sockaddr_in[6] field. | 863 | * is taken from the corresponding sockaddr_in[6] field. |
368 | * | 864 | * |
369 | * @param cfg configuration to use | 865 | * @param cfg configuration to use |
866 | * @param is_tcp GNUNET_YES for TCP, GNUNET_NO for UDP | ||
867 | * @param adv_port advertised port (port we are either bound to or that our OS | ||
868 | * locally performs redirection from to our bound port). | ||
869 | * @param num_addrs number of addresses in 'addrs' | ||
370 | * @param addr the local address packets should be redirected to | 870 | * @param addr the local address packets should be redirected to |
371 | * @param addrlen actual lenght of the address | 871 | * @param addrlen actual lenght of the address |
372 | * @param callback function to call everytime the public IP address changes | 872 | * @param address_callback function to call everytime the public IP address changes |
373 | * @param callback_cls closure for callback | 873 | * @param reversal_callback function to call if someone wants connection reversal from us |
874 | * @param callback_cls closure for callbacks | ||
374 | * @return NULL on error, otherwise handle that can be used to unregister | 875 | * @return NULL on error, otherwise handle that can be used to unregister |
375 | */ | 876 | */ |
376 | struct GNUNET_NAT_Handle * | 877 | struct GNUNET_NAT_Handle * |
377 | GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, | 878 | GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, |
378 | const struct sockaddr *addr, | 879 | int is_tcp, |
379 | socklen_t addrlen, | 880 | uint16_t adv_port, |
380 | GNUNET_NAT_AddressCallback callback, void *callback_cls) | 881 | unsigned int num_addrs, |
882 | const struct sockaddr **addrs, | ||
883 | const socklen_t *addrlens, | ||
884 | GNUNET_NAT_AddressCallback address_callback, | ||
885 | GNUNET_NAT_ReversalCallback reversal_callback, | ||
886 | void *callback_cls) | ||
381 | { | 887 | { |
382 | struct GNUNET_NAT_Handle *h; | 888 | struct GNUNET_NAT_Handle *h; |
889 | struct in_addr in_addr; | ||
890 | unsigned int i; | ||
383 | 891 | ||
384 | h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle)); | 892 | h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle)); |
893 | h->server_retry_delay = GNUNET_TIME_UNIT_SECONDS; | ||
894 | h->cfg = cfg; | ||
895 | h->is_tcp = is_tcp; | ||
896 | h->address_callback = address_callback; | ||
897 | h->reversal_callback = reversal_callback; | ||
898 | h->callback_cls = callback_cls; | ||
899 | h->num_local_addrs = num_addrs; | ||
900 | h->adv_port = adv_port; | ||
901 | if (num_addrs != 0) | ||
902 | { | ||
903 | h->local_addrs = GNUNET_malloc (num_addrs * sizeof (struct sockaddr*)); | ||
904 | h->local_addrlens = GNUNET_malloc (num_addrs * sizeof (socklen_t)); | ||
905 | for (i=0;i<num_addrs;i++) | ||
906 | { | ||
907 | h->local_addrlens[i] = addrlens[i]; | ||
908 | h->local_addrs[i] = GNUNET_malloc (addrlens[i]); | ||
909 | memcpy (h->local_addrs[i], addrs[i], addrlens[i]); | ||
910 | } | ||
911 | } | ||
385 | 912 | ||
386 | if (addr) | 913 | if (GNUNET_OK == |
914 | GNUNET_CONFIGURATION_have_value (cfg, | ||
915 | "nat", | ||
916 | "INTERNAL_ADDRESS")) | ||
387 | { | 917 | { |
388 | GNUNET_assert ((addr->sa_family == AF_INET) || | 918 | (void) GNUNET_CONFIGURATION_get_value_string (cfg, |
389 | (addr->sa_family == AF_INET6)); | 919 | "nat", |
390 | h->local_addr = GNUNET_malloc (addrlen); | 920 | "INTERNAL_ADDRESS", |
391 | memcpy (h->local_addr, addr, addrlen); | 921 | &h->internal_address); |
392 | if (addr->sa_family == AF_INET) | 922 | } |
393 | { | 923 | if ( (h->internal_address != NULL) && |
394 | h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port); | 924 | (inet_pton(AF_INET, h->internal_address, &in_addr) != 1) ) |
395 | ((struct sockaddr_in *) h->local_addr)->sin_port = 0; | 925 | { |
396 | } | 926 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, |
397 | else if (addr->sa_family == AF_INET6) | 927 | "nat", |
398 | { | 928 | _("Malformed %s `%s' given in configuration!\n"), |
399 | h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port); | 929 | "INTERNAL_ADDRESS", |
400 | ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0; | 930 | h->internal_address); |
401 | } | 931 | GNUNET_free (h->internal_address); |
932 | h->internal_address = NULL; | ||
933 | } | ||
934 | |||
935 | if (GNUNET_OK == | ||
936 | GNUNET_CONFIGURATION_have_value (cfg, | ||
937 | "nat", | ||
938 | "EXTERNAL_ADDRESS")) | ||
939 | { | ||
940 | (void) GNUNET_CONFIGURATION_get_value_string (cfg, | ||
941 | "nat", | ||
942 | "EXTERNAL_ADDRESS", | ||
943 | &h->external_address); | ||
944 | } | ||
945 | if ( (h->external_address != NULL) && | ||
946 | (inet_pton(AF_INET, h->external_address, &in_addr) != 1) ) | ||
947 | { | ||
948 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
949 | "nat", | ||
950 | _("Malformed %s `%s' given in configuration!\n"), | ||
951 | "EXTERNAL_ADDRESS", | ||
952 | h->external_address); | ||
953 | GNUNET_free (h->external_address); | ||
954 | h->external_address = NULL; | ||
955 | } | ||
956 | h->behind_nat = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
957 | "nat", | ||
958 | "BEHIND_NAT"); | ||
959 | h->nat_punched = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
960 | "nat", | ||
961 | "NAT_PUNCHED"); | ||
962 | h->enable_nat_client = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
963 | "nat", | ||
964 | "ENABLE_NAT_CLIENT"); | ||
965 | h->enable_nat_server = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
966 | "nat", | ||
967 | "ENABLE_NAT_SERVER"); | ||
968 | h->enable_upnp = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
969 | "nat", | ||
970 | "ENABLE_UPNP"); | ||
971 | h->use_localaddresses = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
972 | "nat", | ||
973 | "USE_LOCALADDR"); | ||
974 | h->disable_ipv6 = GNUNET_CONFIGURATION_get_value_yesno(cfg, | ||
975 | "nat", | ||
976 | "DISABLEV6"); | ||
977 | if (NULL == reversal_callback) | ||
978 | h->enable_nat_server = GNUNET_NO; | ||
979 | |||
980 | /* Check if NAT was hole-punched */ | ||
981 | if ( (NULL != h->address_callback) && | ||
982 | (h->external_address != NULL) && | ||
983 | (h->nat_punched == GNUNET_YES) ) | ||
984 | { | ||
985 | h->ext_dns = GNUNET_RESOLVER_ip_get (h->external_address, | ||
986 | AF_INET, | ||
987 | GNUNET_TIME_UNIT_MINUTES, | ||
988 | &process_external_ip, | ||
989 | h); | ||
990 | h->enable_nat_server = GNUNET_NO; | ||
991 | h->enable_upnp = GNUNET_NO; | ||
992 | } | ||
993 | |||
994 | /* Test for SUID binaries */ | ||
995 | if ( (h->behind_nat == GNUNET_YES) && | ||
996 | (GNUNET_YES == h->enable_nat_server) && | ||
997 | (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server")) ) | ||
998 | { | ||
999 | h->enable_nat_server = GNUNET_NO; | ||
1000 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1001 | _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), | ||
1002 | "gnunet-nat-server"); | ||
1003 | } | ||
1004 | if ( (GNUNET_YES == h->enable_nat_client) && | ||
1005 | (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-client")) ) | ||
1006 | { | ||
1007 | h->enable_nat_client = GNUNET_NO; | ||
1008 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1009 | _("Configuration requires `%s', but binary is not installed properly (SUID bit not set). Option disabled.\n"), | ||
1010 | "gnunet-nat-client"); | ||
1011 | } | ||
1012 | |||
1013 | start_gnunet_nat_server (h); | ||
1014 | |||
1015 | /* FIXME: add support for UPnP, etc */ | ||
1016 | |||
1017 | if (NULL != h->address_callback) | ||
1018 | { | ||
1019 | GNUNET_OS_network_interfaces_list (&process_interfaces, h); | ||
1020 | h->hostname_dns = GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, | ||
1021 | HOSTNAME_RESOLVE_TIMEOUT, | ||
1022 | &process_hostname_ip, | ||
1023 | h); | ||
402 | } | 1024 | } |
403 | h->should_change = GNUNET_YES; | ||
404 | h->is_enabled = GNUNET_YES; | ||
405 | h->upnp_status = GNUNET_NAT_PORT_UNMAPPED; | ||
406 | h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED; | ||
407 | h->callback = callback; | ||
408 | h->callback_cls = callback_cls; | ||
409 | h->upnp = | ||
410 | GNUNET_NAT_UPNP_init (h->local_addr, addrlen, h->public_port, | ||
411 | &upnp_pulse_cb, h); | ||
412 | #if 0 | ||
413 | h->natpmp = | ||
414 | GNUNET_NAT_NATPMP_init (h->local_addr, addrlen, h->public_port, | ||
415 | &natpmp_pulse_cb, h); | ||
416 | #endif | ||
417 | h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
418 | &nat_pulse, h); | ||
419 | return h; | 1025 | return h; |
420 | } | 1026 | } |
421 | 1027 | ||
@@ -429,22 +1035,175 @@ GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, | |||
429 | void | 1035 | void |
430 | GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h) | 1036 | GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h) |
431 | { | 1037 | { |
432 | GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO); | 1038 | unsigned int i; |
433 | GNUNET_NAT_UPNP_close (h->upnp); | 1039 | struct LocalAddressList *lal; |
434 | 1040 | ||
435 | #if 0 | 1041 | if (h->ext_dns != NULL) |
436 | GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO); | 1042 | { |
437 | GNUNET_NAT_NATPMP_close (h->natpmp); | 1043 | GNUNET_RESOLVER_request_cancel (h->ext_dns); |
1044 | h->ext_dns = NULL; | ||
1045 | } | ||
1046 | if (NULL != h->hostname_dns) | ||
1047 | { | ||
1048 | GNUNET_RESOLVER_request_cancel (h->hostname_dns); | ||
1049 | h->hostname_dns = NULL; | ||
1050 | } | ||
1051 | if (GNUNET_SCHEDULER_NO_TASK != h->server_read_task) | ||
1052 | { | ||
1053 | GNUNET_SCHEDULER_cancel (h->server_read_task); | ||
1054 | h->server_read_task = GNUNET_SCHEDULER_NO_TASK; | ||
1055 | } | ||
1056 | if (NULL != h->server_proc) | ||
1057 | { | ||
1058 | if (0 != GNUNET_OS_process_kill (h->server_proc, SIGTERM)) | ||
1059 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
1060 | GNUNET_OS_process_wait (h->server_proc); | ||
1061 | GNUNET_OS_process_close (h->server_proc); | ||
1062 | h->server_proc = NULL; | ||
1063 | GNUNET_DISK_pipe_close (h->server_stdout); | ||
1064 | h->server_stdout = NULL; | ||
1065 | h->server_stdout_handle = NULL; | ||
1066 | } | ||
1067 | if (NULL != h->server_stdout) | ||
1068 | { | ||
1069 | GNUNET_DISK_pipe_close (h->server_stdout); | ||
1070 | h->server_stdout = NULL; | ||
1071 | h->server_stdout_handle = NULL; | ||
1072 | } | ||
1073 | while (NULL != (lal = h->lal_head)) | ||
1074 | { | ||
1075 | GNUNET_CONTAINER_DLL_remove (h->lal_head, | ||
1076 | h->lal_tail, | ||
1077 | lal); | ||
1078 | h->address_callback (h->callback_cls, | ||
1079 | GNUNET_NO, | ||
1080 | (const struct sockaddr*) &lal[1], | ||
1081 | lal->addrlen); | ||
1082 | GNUNET_free (lal); | ||
1083 | } | ||
1084 | for (i=0;i<h->num_local_addrs;i++) | ||
1085 | GNUNET_free (h->local_addrs[i]); | ||
1086 | GNUNET_free_non_null (h->local_addrs); | ||
1087 | GNUNET_free_non_null (h->local_addrlens); | ||
1088 | GNUNET_free_non_null (h->external_address); | ||
1089 | GNUNET_free_non_null (h->internal_address); | ||
1090 | GNUNET_free (h); | ||
1091 | } | ||
1092 | |||
1093 | |||
1094 | /** | ||
1095 | * We learned about a peer (possibly behind NAT) so run the | ||
1096 | * gnunet-nat-client to send dummy ICMP responses to cause | ||
1097 | * that peer to connect to us (connection reversal). | ||
1098 | * | ||
1099 | * @param h NAT handle for us (largely used for configuration) | ||
1100 | * @param sa the address of the peer (IPv4-only) | ||
1101 | */ | ||
1102 | void | ||
1103 | GNUNET_NAT_run_client (struct GNUNET_NAT_Handle *h, | ||
1104 | const struct sockaddr_in *sa) | ||
1105 | { | ||
1106 | char inet4[INET_ADDRSTRLEN]; | ||
1107 | char port_as_string[6]; | ||
1108 | struct GNUNET_OS_Process *proc; | ||
1109 | |||
1110 | if (GNUNET_YES != h->enable_nat_client) | ||
1111 | return; /* not permitted / possible */ | ||
1112 | |||
1113 | if (h->internal_address == NULL) | ||
1114 | { | ||
1115 | GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, | ||
1116 | "nat", | ||
1117 | _("Internal IP address not known, cannot use ICMP NAT traversal method\n")); | ||
1118 | return; | ||
1119 | } | ||
1120 | GNUNET_assert (sa->sin_family == AF_INET); | ||
1121 | if (NULL == inet_ntop (AF_INET, | ||
1122 | &sa->sin_addr, | ||
1123 | inet4, INET_ADDRSTRLEN)) | ||
1124 | { | ||
1125 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); | ||
1126 | return; | ||
1127 | } | ||
1128 | GNUNET_snprintf(port_as_string, | ||
1129 | sizeof (port_as_string), | ||
1130 | "%d", | ||
1131 | h->adv_port); | ||
1132 | #if DEBUG_TCP_NAT | ||
1133 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
1134 | "nat", | ||
1135 | _("Running gnunet-nat-client %s %s %u\n"), | ||
1136 | h->internal_address, | ||
1137 | inet4, | ||
1138 | (unsigned int) h->adv_port); | ||
438 | #endif | 1139 | #endif |
1140 | proc = GNUNET_OS_start_process (NULL, | ||
1141 | NULL, | ||
1142 | "gnunet-nat-client", | ||
1143 | "gnunet-nat-client", | ||
1144 | h->internal_address, | ||
1145 | inet4, | ||
1146 | port_as_string, | ||
1147 | NULL); | ||
1148 | if (NULL == proc) | ||
1149 | return; | ||
1150 | /* we know that the gnunet-nat-client will terminate virtually | ||
1151 | instantly */ | ||
1152 | GNUNET_OS_process_wait (proc); | ||
1153 | GNUNET_OS_process_close (proc); | ||
1154 | } | ||
439 | 1155 | ||
440 | if (GNUNET_SCHEDULER_NO_TASK != h->pulse_timer) | ||
441 | GNUNET_SCHEDULER_cancel (h->pulse_timer); | ||
442 | 1156 | ||
443 | GNUNET_free_non_null (h->local_addr); | 1157 | /** |
444 | GNUNET_free_non_null (h->ext_addr); | 1158 | * Test if the given address is (currently) a plausible IP address for this peer. |
445 | GNUNET_free_non_null (h->ext_addr_upnp); | 1159 | * |
446 | GNUNET_free_non_null (h->ext_addr_natpmp); | 1160 | * @param h the handle returned by register |
447 | GNUNET_free (h); | 1161 | * @param addr IP address to test (IPv4 or IPv6) |
1162 | * @param addrlen number of bytes in addr | ||
1163 | * @return GNUNET_YES if the address is plausible, | ||
1164 | * GNUNET_NO if the address is not plausible, | ||
1165 | * GNUNET_SYSERR if the address is malformed | ||
1166 | */ | ||
1167 | int | ||
1168 | GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *h, | ||
1169 | const void *addr, | ||
1170 | socklen_t addrlen) | ||
1171 | { | ||
1172 | struct LocalAddressList *pos; | ||
1173 | const struct sockaddr_in *in4; | ||
1174 | const struct sockaddr_in6 *in6; | ||
1175 | |||
1176 | if ( (addrlen != sizeof (struct in_addr)) && | ||
1177 | (addrlen != sizeof (struct in6_addr)) ) | ||
1178 | { | ||
1179 | GNUNET_break (0); | ||
1180 | return GNUNET_SYSERR; | ||
1181 | } | ||
1182 | pos = h->lal_head; | ||
1183 | while (NULL != pos) | ||
1184 | { | ||
1185 | if (pos->addrlen == sizeof (struct sockaddr_in)) | ||
1186 | { | ||
1187 | in4 = (struct sockaddr_in* ) &pos[1]; | ||
1188 | if ( (addrlen == sizeof (struct in_addr)) && | ||
1189 | (0 == memcmp (&in4->sin_addr, addr, sizeof (struct in_addr))) ) | ||
1190 | return GNUNET_YES; | ||
1191 | } | ||
1192 | else if (pos->addrlen == sizeof (struct sockaddr_in6)) | ||
1193 | { | ||
1194 | in6 = (struct sockaddr_in6* ) &pos[1]; | ||
1195 | if ( (addrlen == sizeof (struct in6_addr)) && | ||
1196 | (0 == memcmp (&in6->sin6_addr, addr, sizeof (struct in6_addr))) ) | ||
1197 | return GNUNET_YES; | ||
1198 | } | ||
1199 | else | ||
1200 | { | ||
1201 | GNUNET_assert (0); | ||
1202 | } | ||
1203 | pos = pos->next; | ||
1204 | } | ||
1205 | return GNUNET_YES; | ||
448 | } | 1206 | } |
449 | 1207 | ||
1208 | |||
450 | /* end of nat.c */ | 1209 | /* end of nat.c */ |