diff options
Diffstat (limited to 'src/nat/gnunet-service-nat.c')
-rw-r--r-- | src/nat/gnunet-service-nat.c | 2083 |
1 files changed, 0 insertions, 2083 deletions
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c deleted file mode 100644 index 4dcc0312f..000000000 --- a/src/nat/gnunet-service-nat.c +++ /dev/null | |||
@@ -1,2083 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2016, 2017 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file nat/gnunet-service-nat.c | ||
23 | * @brief network address translation traversal service | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * The purpose of this service is to enable transports to | ||
27 | * traverse NAT routers, by providing traversal options and | ||
28 | * knowledge about the local network topology. | ||
29 | * | ||
30 | * TODO: | ||
31 | * - migrate test cases to new NAT service | ||
32 | * - add new traceroute-based logic for external IP detection | ||
33 | * | ||
34 | * - implement & test STUN processing to classify NAT; | ||
35 | * basically, open port & try different methods. | ||
36 | */ | ||
37 | #include "platform.h" | ||
38 | #include <math.h> | ||
39 | #include "gnunet_util_lib.h" | ||
40 | #include "gnunet_protocols.h" | ||
41 | #include "gnunet_signatures.h" | ||
42 | #include "gnunet_statistics_service.h" | ||
43 | #include "gnunet_resolver_service.h" | ||
44 | #include "gnunet_nat_service.h" | ||
45 | #include "gnunet-service-nat.h" | ||
46 | #include "gnunet-service-nat_externalip.h" | ||
47 | #include "gnunet-service-nat_stun.h" | ||
48 | #include "gnunet-service-nat_mini.h" | ||
49 | #include "gnunet-service-nat_helper.h" | ||
50 | #include "nat.h" | ||
51 | #include <gcrypt.h> | ||
52 | |||
53 | |||
54 | /** | ||
55 | * How often should we ask the OS about a list of active | ||
56 | * network interfaces? | ||
57 | */ | ||
58 | #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) | ||
59 | |||
60 | /** | ||
61 | * How long do we wait until we forcefully terminate autoconfiguration? | ||
62 | */ | ||
63 | #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
64 | GNUNET_TIME_UNIT_SECONDS, 5) | ||
65 | |||
66 | /** | ||
67 | * How often do we scan for changes in how our external (dyndns) hostname resolves? | ||
68 | */ | ||
69 | #define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply ( \ | ||
70 | GNUNET_TIME_UNIT_MINUTES, 7) | ||
71 | |||
72 | |||
73 | /** | ||
74 | * Information we track per client address. | ||
75 | */ | ||
76 | struct ClientAddress | ||
77 | { | ||
78 | /** | ||
79 | * Network address used by the client. | ||
80 | */ | ||
81 | struct sockaddr_storage ss; | ||
82 | |||
83 | /** | ||
84 | * Handle to active UPnP request where we asked upnpc to open | ||
85 | * a port at the NAT. NULL if we do not have such a request | ||
86 | * pending. | ||
87 | */ | ||
88 | struct GNUNET_NAT_MiniHandle *mh; | ||
89 | }; | ||
90 | |||
91 | |||
92 | /** | ||
93 | * List of local addresses this system has. | ||
94 | */ | ||
95 | struct LocalAddressList | ||
96 | { | ||
97 | /** | ||
98 | * This is a linked list. | ||
99 | */ | ||
100 | struct LocalAddressList *next; | ||
101 | |||
102 | /** | ||
103 | * Previous entry. | ||
104 | */ | ||
105 | struct LocalAddressList *prev; | ||
106 | |||
107 | /** | ||
108 | * Context for a gnunet-helper-nat-server used to listen | ||
109 | * for ICMP messages to this client for connection reversal. | ||
110 | */ | ||
111 | struct HelperContext *hc; | ||
112 | |||
113 | /** | ||
114 | * The address itself (i.e. `struct sockaddr_in` or `struct | ||
115 | * sockaddr_in6`, in the respective byte order). | ||
116 | */ | ||
117 | struct sockaddr_storage addr; | ||
118 | |||
119 | /** | ||
120 | * Address family. (FIXME: redundant, addr.ss_family! Remove!?) | ||
121 | */ | ||
122 | int af; | ||
123 | |||
124 | /** | ||
125 | * #GNUNET_YES if we saw this one in the previous iteration, | ||
126 | * but not in the current iteration and thus might need to | ||
127 | * remove it at the end. | ||
128 | */ | ||
129 | int old; | ||
130 | |||
131 | /** | ||
132 | * What type of address is this? | ||
133 | */ | ||
134 | enum GNUNET_NAT_AddressClass ac; | ||
135 | }; | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Internal data structure we track for each of our clients. | ||
140 | */ | ||
141 | struct ClientHandle | ||
142 | { | ||
143 | /** | ||
144 | * Kept in a DLL. | ||
145 | */ | ||
146 | struct ClientHandle *next; | ||
147 | |||
148 | /** | ||
149 | * Kept in a DLL. | ||
150 | */ | ||
151 | struct ClientHandle *prev; | ||
152 | |||
153 | /** | ||
154 | * Underlying handle for this client with the service. | ||
155 | */ | ||
156 | struct GNUNET_SERVICE_Client *client; | ||
157 | |||
158 | /** | ||
159 | * Message queue for communicating with the client. | ||
160 | */ | ||
161 | struct GNUNET_MQ_Handle *mq; | ||
162 | |||
163 | /** | ||
164 | * Array of addresses used by the service. | ||
165 | */ | ||
166 | struct ClientAddress *caddrs; | ||
167 | |||
168 | /** | ||
169 | * External DNS name and port given by user due to manual | ||
170 | * hole punching. Special DNS name 'AUTO' is used to indicate | ||
171 | * desire for automatic determination of the external IP | ||
172 | * (instead of DNS or manual configuration, i.e. to be used | ||
173 | * if the IP keeps changing and we have no DynDNS, but we do | ||
174 | * have a hole punched). | ||
175 | */ | ||
176 | char *hole_external; | ||
177 | |||
178 | /** | ||
179 | * Name of the configuration section this client cares about. | ||
180 | */ | ||
181 | char *section_name; | ||
182 | |||
183 | /** | ||
184 | * Task for periodically re-running the @e ext_dns DNS lookup. | ||
185 | */ | ||
186 | struct GNUNET_SCHEDULER_Task *ext_dns_task; | ||
187 | |||
188 | /** | ||
189 | * Handle for (DYN)DNS lookup of our external IP as given in | ||
190 | * @e hole_external. | ||
191 | */ | ||
192 | struct GNUNET_RESOLVER_RequestHandle *ext_dns; | ||
193 | |||
194 | /** | ||
195 | * Handle for monitoring external IP changes. | ||
196 | */ | ||
197 | struct GN_ExternalIPMonitor *external_monitor; | ||
198 | |||
199 | /** | ||
200 | * DLL of external IP addresses as given in @e hole_external. | ||
201 | */ | ||
202 | struct LocalAddressList *ext_addr_head; | ||
203 | |||
204 | /** | ||
205 | * DLL of external IP addresses as given in @e hole_external. | ||
206 | */ | ||
207 | struct LocalAddressList *ext_addr_tail; | ||
208 | |||
209 | /** | ||
210 | * Port number we found in @e hole_external. | ||
211 | */ | ||
212 | uint16_t ext_dns_port; | ||
213 | |||
214 | /** | ||
215 | * What does this client care about? | ||
216 | */ | ||
217 | enum GNUNET_NAT_RegisterFlags flags; | ||
218 | |||
219 | /** | ||
220 | * Is any of the @e caddrs in a reserved subnet for NAT? | ||
221 | */ | ||
222 | int natted_address; | ||
223 | |||
224 | /** | ||
225 | * Number of addresses that this service is bound to. | ||
226 | * Length of the @e caddrs array. | ||
227 | */ | ||
228 | uint16_t num_caddrs; | ||
229 | |||
230 | /** | ||
231 | * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. | ||
232 | */ | ||
233 | uint8_t proto; | ||
234 | }; | ||
235 | |||
236 | |||
237 | /** | ||
238 | * External IP address as given to us via some STUN server. | ||
239 | */ | ||
240 | struct StunExternalIP | ||
241 | { | ||
242 | /** | ||
243 | * Kept in a DLL. | ||
244 | */ | ||
245 | struct StunExternalIP *next; | ||
246 | |||
247 | /** | ||
248 | * Kept in a DLL. | ||
249 | */ | ||
250 | struct StunExternalIP *prev; | ||
251 | |||
252 | /** | ||
253 | * Task we run to remove this entry when it is stale. | ||
254 | */ | ||
255 | struct GNUNET_SCHEDULER_Task *timeout_task; | ||
256 | |||
257 | /** | ||
258 | * Our external IP address as reported by the | ||
259 | * STUN server. | ||
260 | */ | ||
261 | struct sockaddr_in external_addr; | ||
262 | |||
263 | /** | ||
264 | * Address of the reporting STUN server. Used to | ||
265 | * detect when a STUN server changes its opinion | ||
266 | * to more quickly remove stale results. | ||
267 | */ | ||
268 | struct sockaddr_storage stun_server_addr; | ||
269 | |||
270 | /** | ||
271 | * Number of bytes used in @e stun_server_addr. | ||
272 | */ | ||
273 | size_t stun_server_addr_len; | ||
274 | }; | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Timeout to use when STUN data is considered stale. | ||
279 | */ | ||
280 | static struct GNUNET_TIME_Relative stun_stale_timeout; | ||
281 | |||
282 | /** | ||
283 | * How often do we scan for changes in how our external (dyndns) hostname resolves? | ||
284 | */ | ||
285 | static struct GNUNET_TIME_Relative dyndns_frequency; | ||
286 | |||
287 | /** | ||
288 | * Handle to our current configuration. | ||
289 | */ | ||
290 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
291 | |||
292 | /** | ||
293 | * Handle to the statistics service. | ||
294 | */ | ||
295 | static struct GNUNET_STATISTICS_Handle *stats; | ||
296 | |||
297 | /** | ||
298 | * Task scheduled to periodically scan our network interfaces. | ||
299 | */ | ||
300 | static struct GNUNET_SCHEDULER_Task *scan_task; | ||
301 | |||
302 | /** | ||
303 | * Head of client DLL. | ||
304 | */ | ||
305 | static struct ClientHandle *ch_head; | ||
306 | |||
307 | /** | ||
308 | * Tail of client DLL. | ||
309 | */ | ||
310 | static struct ClientHandle *ch_tail; | ||
311 | |||
312 | /** | ||
313 | * Head of DLL of local addresses. | ||
314 | */ | ||
315 | static struct LocalAddressList *lal_head; | ||
316 | |||
317 | /** | ||
318 | * Tail of DLL of local addresses. | ||
319 | */ | ||
320 | static struct LocalAddressList *lal_tail; | ||
321 | |||
322 | /** | ||
323 | * Kept in a DLL. | ||
324 | */ | ||
325 | static struct StunExternalIP *se_head; | ||
326 | |||
327 | /** | ||
328 | * Kept in a DLL. | ||
329 | */ | ||
330 | static struct StunExternalIP *se_tail; | ||
331 | |||
332 | /** | ||
333 | * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, | ||
334 | * #GNUNET_SYSERR if configuration enabled but binary is unavailable. | ||
335 | */ | ||
336 | int enable_upnp; | ||
337 | |||
338 | /** | ||
339 | * Is IP Scanning enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, | ||
340 | * without, only explicitly specified IPs will be handled (HOLE_EXTERNAL) | ||
341 | */ | ||
342 | int enable_ipscan; | ||
343 | |||
344 | /** | ||
345 | * Remove and free an entry from the #lal_head DLL. | ||
346 | * | ||
347 | * @param lal entry to free | ||
348 | */ | ||
349 | static void | ||
350 | free_lal (struct LocalAddressList *lal) | ||
351 | { | ||
352 | GNUNET_CONTAINER_DLL_remove (lal_head, | ||
353 | lal_tail, | ||
354 | lal); | ||
355 | if (NULL != lal->hc) | ||
356 | { | ||
357 | GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, | ||
358 | "Lost NATed local address %s, stopping NAT server\n", | ||
359 | GNUNET_a2s ((const struct sockaddr *) &lal->addr, | ||
360 | sizeof(struct sockaddr_in))); | ||
361 | |||
362 | GN_stop_gnunet_nat_server_ (lal->hc); | ||
363 | lal->hc = NULL; | ||
364 | } | ||
365 | GNUNET_free (lal); | ||
366 | } | ||
367 | |||
368 | |||
369 | /** | ||
370 | * Free the DLL starting at #lal_head. | ||
371 | */ | ||
372 | static void | ||
373 | destroy_lal () | ||
374 | { | ||
375 | struct LocalAddressList *lal; | ||
376 | |||
377 | while (NULL != (lal = lal_head)) | ||
378 | free_lal (lal); | ||
379 | } | ||
380 | |||
381 | |||
382 | /** | ||
383 | * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from | ||
384 | * client. | ||
385 | * | ||
386 | * @param cls client who sent the message | ||
387 | * @param message the message received | ||
388 | * @return #GNUNET_OK if message is well-formed | ||
389 | */ | ||
390 | static int | ||
391 | check_register (void *cls, | ||
392 | const struct GNUNET_NAT_RegisterMessage *message) | ||
393 | { | ||
394 | uint16_t num_addrs = ntohs (message->num_addrs); | ||
395 | const char *off = (const char *) &message[1]; | ||
396 | size_t left = ntohs (message->header.size) - sizeof(*message); | ||
397 | |||
398 | for (unsigned int i = 0; i < num_addrs; i++) | ||
399 | { | ||
400 | size_t alen; | ||
401 | const struct sockaddr *sa = (const struct sockaddr *) off; | ||
402 | |||
403 | if (sizeof(sa_family_t) > left) | ||
404 | { | ||
405 | GNUNET_break (0); | ||
406 | return GNUNET_SYSERR; | ||
407 | } | ||
408 | switch (sa->sa_family) | ||
409 | { | ||
410 | case AF_INET: | ||
411 | alen = sizeof(struct sockaddr_in); | ||
412 | break; | ||
413 | |||
414 | case AF_INET6: | ||
415 | alen = sizeof(struct sockaddr_in6); | ||
416 | break; | ||
417 | |||
418 | #if AF_UNIX | ||
419 | case AF_UNIX: | ||
420 | alen = sizeof(struct sockaddr_un); | ||
421 | break; | ||
422 | #endif | ||
423 | default: | ||
424 | GNUNET_break (0); | ||
425 | return GNUNET_SYSERR; | ||
426 | } | ||
427 | if (alen > left) | ||
428 | { | ||
429 | GNUNET_break (0); | ||
430 | return GNUNET_SYSERR; | ||
431 | } | ||
432 | off += alen; | ||
433 | left -= alen; | ||
434 | } | ||
435 | if (left != ntohs (message->str_len)) | ||
436 | { | ||
437 | GNUNET_break (0); | ||
438 | return GNUNET_SYSERR; | ||
439 | } | ||
440 | return GNUNET_OK; | ||
441 | } | ||
442 | |||
443 | |||
444 | /** | ||
445 | * Check if @a ip is in @a network with @a bits netmask. | ||
446 | * | ||
447 | * @param network to test | ||
448 | * @param ip IP address to test | ||
449 | * @param bits bitmask for the network | ||
450 | * @return #GNUNET_YES if @a ip is in @a network | ||
451 | */ | ||
452 | static int | ||
453 | match_ipv4 (const char *network, | ||
454 | const struct in_addr *ip, | ||
455 | uint8_t bits) | ||
456 | { | ||
457 | struct in_addr net; | ||
458 | |||
459 | if (0 == ip->s_addr) | ||
460 | return GNUNET_YES; | ||
461 | if (0 == bits) | ||
462 | return GNUNET_YES; | ||
463 | GNUNET_assert (1 == inet_pton (AF_INET, | ||
464 | network, | ||
465 | &net)); | ||
466 | return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); | ||
467 | } | ||
468 | |||
469 | |||
470 | /** | ||
471 | * Check if @a ip is in @a network with @a bits netmask. | ||
472 | * | ||
473 | * @param network to test | ||
474 | * @param ip IP address to test | ||
475 | * @param bits bitmask for the network | ||
476 | * @return #GNUNET_YES if @a ip is in @a network | ||
477 | */ | ||
478 | static int | ||
479 | match_ipv6 (const char *network, | ||
480 | const struct in6_addr *ip, | ||
481 | uint8_t bits) | ||
482 | { | ||
483 | struct in6_addr net; | ||
484 | struct in6_addr mask; | ||
485 | unsigned int off; | ||
486 | |||
487 | if (0 == bits) | ||
488 | return GNUNET_YES; | ||
489 | GNUNET_assert (1 == inet_pton (AF_INET6, | ||
490 | network, | ||
491 | &net)); | ||
492 | memset (&mask, 0, sizeof(mask)); | ||
493 | if (0 == GNUNET_memcmp (&mask, | ||
494 | ip)) | ||
495 | return GNUNET_YES; | ||
496 | off = 0; | ||
497 | while (bits > 8) | ||
498 | { | ||
499 | mask.s6_addr[off++] = 0xFF; | ||
500 | bits -= 8; | ||
501 | } | ||
502 | while (bits > 0) | ||
503 | { | ||
504 | mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; | ||
505 | bits--; | ||
506 | } | ||
507 | for (unsigned j = 0; j < sizeof(struct in6_addr) / sizeof(uint32_t); j++) | ||
508 | if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != | ||
509 | (((uint32_t *) &net)[j] & ((int *) &mask)[j])) | ||
510 | return GNUNET_NO; | ||
511 | return GNUNET_YES; | ||
512 | } | ||
513 | |||
514 | |||
515 | /** | ||
516 | * Test if the given IPv4 address is in a known range | ||
517 | * for private networks. | ||
518 | * | ||
519 | * @param ip address to test | ||
520 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
521 | */ | ||
522 | static int | ||
523 | is_nat_v4 (const struct in_addr *ip) | ||
524 | { | ||
525 | return | ||
526 | match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ | ||
527 | match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ | ||
528 | match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ | ||
529 | match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ | ||
530 | match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ | ||
531 | } | ||
532 | |||
533 | |||
534 | /** | ||
535 | * Test if the given IPv6 address is in a known range | ||
536 | * for private networks. | ||
537 | * | ||
538 | * @param ip address to test | ||
539 | * @return #GNUNET_YES if @a ip is in a NAT range | ||
540 | */ | ||
541 | static int | ||
542 | is_nat_v6 (const struct in6_addr *ip) | ||
543 | { | ||
544 | return | ||
545 | match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ | ||
546 | match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ | ||
547 | match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ | ||
548 | } | ||
549 | |||
550 | |||
551 | /** | ||
552 | * Closure for #ifc_proc. | ||
553 | */ | ||
554 | struct IfcProcContext | ||
555 | { | ||
556 | /** | ||
557 | * Head of DLL of local addresses. | ||
558 | */ | ||
559 | struct LocalAddressList *lal_head; | ||
560 | |||
561 | /** | ||
562 | * Tail of DLL of local addresses. | ||
563 | */ | ||
564 | struct LocalAddressList *lal_tail; | ||
565 | }; | ||
566 | |||
567 | |||
568 | /** | ||
569 | * Callback function invoked for each interface found. Adds them | ||
570 | * to our new address list. | ||
571 | * | ||
572 | * @param cls a `struct IfcProcContext *` | ||
573 | * @param name name of the interface (can be NULL for unknown) | ||
574 | * @param isDefault is this presumably the default interface | ||
575 | * @param addr address of this interface (can be NULL for unknown or unassigned) | ||
576 | * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) | ||
577 | * @param netmask the network mask (can be NULL for unknown or unassigned) | ||
578 | * @param addrlen length of the address | ||
579 | * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort | ||
580 | */ | ||
581 | static int | ||
582 | ifc_proc (void *cls, | ||
583 | const char *name, | ||
584 | int isDefault, | ||
585 | const struct sockaddr *addr, | ||
586 | const struct sockaddr *broadcast_addr, | ||
587 | const struct sockaddr *netmask, | ||
588 | socklen_t addrlen) | ||
589 | { | ||
590 | struct IfcProcContext *ifc_ctx = cls; | ||
591 | struct LocalAddressList *lal; | ||
592 | size_t alen; | ||
593 | const struct in_addr *ip4; | ||
594 | const struct in6_addr *ip6; | ||
595 | enum GNUNET_NAT_AddressClass ac; | ||
596 | |||
597 | switch (addr->sa_family) | ||
598 | { | ||
599 | case AF_INET: | ||
600 | alen = sizeof(struct sockaddr_in); | ||
601 | ip4 = &((const struct sockaddr_in *) addr)->sin_addr; | ||
602 | if (match_ipv4 ("127.0.0.0", ip4, 8)) | ||
603 | ac = GNUNET_NAT_AC_LOOPBACK; | ||
604 | else if (is_nat_v4 (ip4)) | ||
605 | ac = GNUNET_NAT_AC_LAN; | ||
606 | else | ||
607 | ac = GNUNET_NAT_AC_GLOBAL; | ||
608 | break; | ||
609 | |||
610 | case AF_INET6: | ||
611 | alen = sizeof(struct sockaddr_in6); | ||
612 | ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; | ||
613 | if (match_ipv6 ("::1", ip6, 128)) | ||
614 | ac = GNUNET_NAT_AC_LOOPBACK; | ||
615 | else if (is_nat_v6 (ip6)) | ||
616 | ac = GNUNET_NAT_AC_LAN; | ||
617 | else | ||
618 | ac = GNUNET_NAT_AC_GLOBAL; | ||
619 | if ((ip6->s6_addr[11] == 0xFF) && | ||
620 | (ip6->s6_addr[12] == 0xFE)) | ||
621 | { | ||
622 | /* contains a MAC, be extra careful! */ | ||
623 | ac |= GNUNET_NAT_AC_PRIVATE; | ||
624 | } | ||
625 | break; | ||
626 | |||
627 | #if AF_UNIX | ||
628 | case AF_UNIX: | ||
629 | GNUNET_break (0); | ||
630 | return GNUNET_OK; | ||
631 | #endif | ||
632 | default: | ||
633 | GNUNET_break (0); | ||
634 | return GNUNET_OK; | ||
635 | } | ||
636 | lal = GNUNET_malloc (sizeof(*lal)); | ||
637 | lal->af = addr->sa_family; | ||
638 | lal->ac = ac; | ||
639 | GNUNET_memcpy (&lal->addr, | ||
640 | addr, | ||
641 | alen); | ||
642 | GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head, | ||
643 | ifc_ctx->lal_tail, | ||
644 | lal); | ||
645 | return GNUNET_OK; | ||
646 | } | ||
647 | |||
648 | |||
649 | /** | ||
650 | * Notify client about a change in the list of addresses this peer | ||
651 | * has. | ||
652 | * | ||
653 | * @param ac address class of the entry in the list that changed | ||
654 | * @param ch client to contact | ||
655 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
656 | * @param addr the address that changed | ||
657 | * @param addr_len number of bytes in @a addr | ||
658 | */ | ||
659 | static void | ||
660 | notify_client (enum GNUNET_NAT_AddressClass ac, | ||
661 | struct ClientHandle *ch, | ||
662 | int add, | ||
663 | const void *addr, | ||
664 | size_t addr_len) | ||
665 | { | ||
666 | struct GNUNET_MQ_Envelope *env; | ||
667 | struct GNUNET_NAT_AddressChangeNotificationMessage *msg; | ||
668 | |||
669 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
670 | "Notifying client about %s of IP %s\n", | ||
671 | add ? "addition" : "removal", | ||
672 | GNUNET_a2s (addr, | ||
673 | addr_len)); | ||
674 | env = GNUNET_MQ_msg_extra (msg, | ||
675 | addr_len, | ||
676 | GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); | ||
677 | msg->add_remove = htonl (add); | ||
678 | msg->addr_class = htonl (ac); | ||
679 | GNUNET_memcpy (&msg[1], | ||
680 | addr, | ||
681 | addr_len); | ||
682 | GNUNET_MQ_send (ch->mq, | ||
683 | env); | ||
684 | } | ||
685 | |||
686 | |||
687 | /** | ||
688 | * Check if we should bother to notify this client about this | ||
689 | * address change, and if so, do it. | ||
690 | * | ||
691 | * @param delta the entry in the list that changed | ||
692 | * @param ch client to check | ||
693 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
694 | */ | ||
695 | static void | ||
696 | check_notify_client (struct LocalAddressList *delta, | ||
697 | struct ClientHandle *ch, | ||
698 | int add) | ||
699 | { | ||
700 | size_t alen; | ||
701 | struct sockaddr_in v4; | ||
702 | struct sockaddr_in6 v6; | ||
703 | |||
704 | if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) | ||
705 | { | ||
706 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
707 | "Not notifying client as it does not care about addresses\n"); | ||
708 | return; | ||
709 | } | ||
710 | switch (delta->af) | ||
711 | { | ||
712 | case AF_INET: | ||
713 | alen = sizeof(struct sockaddr_in); | ||
714 | GNUNET_memcpy (&v4, | ||
715 | &delta->addr, | ||
716 | alen); | ||
717 | |||
718 | /* Check for client notifications */ | ||
719 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
720 | { | ||
721 | const struct sockaddr_in *c4; | ||
722 | |||
723 | if (AF_INET != ch->caddrs[i].ss.ss_family) | ||
724 | continue; /* IPv4 not relevant */ | ||
725 | c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss; | ||
726 | if (match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) && | ||
727 | (0 != c4->sin_addr.s_addr) && | ||
728 | (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8))) | ||
729 | continue; /* bound to loopback, but this is not loopback */ | ||
730 | if ((! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8)) && | ||
731 | match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) | ||
732 | continue; /* bound to non-loopback, but this is loopback */ | ||
733 | if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && | ||
734 | (0 != c4->sin_addr.s_addr) && | ||
735 | (! is_nat_v4 (&v4.sin_addr))) | ||
736 | continue; /* based on external-IP, but this IP is not | ||
737 | from private address range. */ | ||
738 | if ((0 != GNUNET_memcmp (&v4.sin_addr, | ||
739 | &c4->sin_addr)) && | ||
740 | (0 != c4->sin_addr.s_addr) && | ||
741 | (! is_nat_v4 (&c4->sin_addr))) | ||
742 | continue; /* this IP is not from private address range, | ||
743 | and IP does not match. */ | ||
744 | |||
745 | /* OK, IP seems relevant, notify client */ | ||
746 | if (0 == htons (v4.sin_port)) | ||
747 | v4.sin_port = c4->sin_port; | ||
748 | notify_client (delta->ac, | ||
749 | ch, | ||
750 | add, | ||
751 | &v4, | ||
752 | alen); | ||
753 | } | ||
754 | break; | ||
755 | |||
756 | case AF_INET6: | ||
757 | alen = sizeof(struct sockaddr_in6); | ||
758 | GNUNET_memcpy (&v6, | ||
759 | &delta->addr, | ||
760 | alen); | ||
761 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
762 | { | ||
763 | const struct sockaddr_in6 *c6; | ||
764 | |||
765 | if (AF_INET6 != ch->caddrs[i].ss.ss_family) | ||
766 | continue; /* IPv4 not relevant */ | ||
767 | c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss; | ||
768 | if (match_ipv6 ("::1", &c6->sin6_addr, 128) && | ||
769 | (0 != GNUNET_memcmp (&c6->sin6_addr, | ||
770 | &in6addr_any)) && | ||
771 | (! match_ipv6 ("::1", &v6.sin6_addr, 128))) | ||
772 | continue; /* bound to loopback, but this is not loopback */ | ||
773 | if ((! match_ipv6 ("::1", &c6->sin6_addr, 128)) && | ||
774 | match_ipv6 ("::1", &v6.sin6_addr, 128)) | ||
775 | continue; /* bound to non-loopback, but this is loopback */ | ||
776 | if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && | ||
777 | (0 != GNUNET_memcmp (&c6->sin6_addr, | ||
778 | &in6addr_any)) && | ||
779 | (! is_nat_v6 (&v6.sin6_addr))) | ||
780 | continue; /* based on external-IP, but this IP is not | ||
781 | from private address range. */ | ||
782 | if ((0 != GNUNET_memcmp (&v6.sin6_addr, | ||
783 | &c6->sin6_addr)) && | ||
784 | (0 != GNUNET_memcmp (&c6->sin6_addr, | ||
785 | &in6addr_any)) && | ||
786 | (! is_nat_v6 (&c6->sin6_addr))) | ||
787 | continue; /* this IP is not from private address range, | ||
788 | and IP does not match. */ | ||
789 | if ((match_ipv6 ("fe80::", &c6->sin6_addr, 10)) && | ||
790 | (0 != GNUNET_memcmp (&c6->sin6_addr, | ||
791 | &in6addr_any)) && | ||
792 | (0 != GNUNET_memcmp (&v6.sin6_addr, | ||
793 | &c6->sin6_addr)) && | ||
794 | (0 == (delta->ac & GNUNET_NAT_AC_EXTERN))) | ||
795 | continue; /* client bound to link-local, and the other address | ||
796 | does not match and is not an external IP */ | ||
797 | |||
798 | /* OK, IP seems relevant, notify client */ | ||
799 | if (0 == htons (v6.sin6_port)) | ||
800 | v6.sin6_port = c6->sin6_port; | ||
801 | notify_client (delta->ac, | ||
802 | ch, | ||
803 | add, | ||
804 | &v6, | ||
805 | alen); | ||
806 | } | ||
807 | break; | ||
808 | |||
809 | default: | ||
810 | GNUNET_break (0); | ||
811 | return; | ||
812 | } | ||
813 | } | ||
814 | |||
815 | |||
816 | /** | ||
817 | * Notify all clients about a change in the list | ||
818 | * of addresses this peer has. | ||
819 | * | ||
820 | * @param delta the entry in the list that changed | ||
821 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
822 | */ | ||
823 | static void | ||
824 | notify_clients (struct LocalAddressList *delta, | ||
825 | int add) | ||
826 | { | ||
827 | for (struct ClientHandle *ch = ch_head; | ||
828 | NULL != ch; | ||
829 | ch = ch->next) | ||
830 | check_notify_client (delta, | ||
831 | ch, | ||
832 | add); | ||
833 | } | ||
834 | |||
835 | |||
836 | /** | ||
837 | * Tell relevant client about a change in our external | ||
838 | * IPv4 address. | ||
839 | * | ||
840 | * @param cls client to check if it cares and possibly notify | ||
841 | * @param v4 the external address that changed | ||
842 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
843 | */ | ||
844 | static void | ||
845 | notify_client_external_ipv4_change (void *cls, | ||
846 | const struct in_addr *v4, | ||
847 | int add) | ||
848 | { | ||
849 | struct ClientHandle *ch = cls; | ||
850 | struct sockaddr_in sa; | ||
851 | int have_v4; | ||
852 | |||
853 | /* (0) check if this impacts 'hole_external' */ | ||
854 | if ((NULL != ch->hole_external) && | ||
855 | (0 == strcasecmp (ch->hole_external, | ||
856 | "AUTO"))) | ||
857 | { | ||
858 | struct LocalAddressList lal; | ||
859 | struct sockaddr_in *s4; | ||
860 | |||
861 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
862 | "Detected eternal IP, can now back-fill AUTO:%u in hole punching specification of `%s'\n", | ||
863 | (unsigned int) ch->ext_dns_port, | ||
864 | ch->section_name); | ||
865 | memset (&lal, 0, sizeof(lal)); | ||
866 | s4 = (struct sockaddr_in *) &lal.addr; | ||
867 | s4->sin_family = AF_INET; | ||
868 | s4->sin_port = htons (ch->ext_dns_port); | ||
869 | s4->sin_addr = *v4; | ||
870 | lal.af = AF_INET; | ||
871 | lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; | ||
872 | check_notify_client (&lal, | ||
873 | ch, | ||
874 | add); | ||
875 | } | ||
876 | |||
877 | /* (1) check if client cares. */ | ||
878 | if (! ch->natted_address) | ||
879 | return; | ||
880 | have_v4 = GNUNET_NO; | ||
881 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
882 | { | ||
883 | const struct sockaddr_storage *ss = &ch->caddrs[i].ss; | ||
884 | |||
885 | if (AF_INET != ss->ss_family) | ||
886 | continue; | ||
887 | have_v4 = GNUNET_YES; | ||
888 | break; | ||
889 | } | ||
890 | if (GNUNET_NO == have_v4) | ||
891 | return; /* IPv6-only */ | ||
892 | |||
893 | /* (2) build address info */ | ||
894 | memset (&sa, | ||
895 | 0, | ||
896 | sizeof(sa)); | ||
897 | sa.sin_family = AF_INET; | ||
898 | sa.sin_addr = *v4; | ||
899 | sa.sin_port = htons (0); | ||
900 | |||
901 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
902 | "Detected eternal IP %s, notifying client of external IP (without port)\n", | ||
903 | GNUNET_a2s ((const struct sockaddr *) &sa, | ||
904 | sizeof(sa))); | ||
905 | /* (3) notify client of change */ | ||
906 | notify_client (is_nat_v4 (v4) | ||
907 | ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN | ||
908 | : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL, | ||
909 | ch, | ||
910 | add, | ||
911 | &sa, | ||
912 | sizeof(sa)); | ||
913 | } | ||
914 | |||
915 | |||
916 | /** | ||
917 | * We got a connection reversal request from another peer. | ||
918 | * Notify applicable clients. | ||
919 | * | ||
920 | * @param cls closure with the `struct LocalAddressList` | ||
921 | * @param ra IP address of the peer who wants us to connect to it | ||
922 | */ | ||
923 | static void | ||
924 | reversal_callback (void *cls, | ||
925 | const struct sockaddr_in *ra) | ||
926 | { | ||
927 | struct LocalAddressList *lal = cls; | ||
928 | const struct sockaddr_in *l4; | ||
929 | |||
930 | GNUNET_assert (AF_INET == lal->af); | ||
931 | l4 = (const struct sockaddr_in *) &lal->addr; | ||
932 | for (struct ClientHandle *ch = ch_head; | ||
933 | NULL != ch; | ||
934 | ch = ch->next) | ||
935 | { | ||
936 | struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm; | ||
937 | struct GNUNET_MQ_Envelope *env; | ||
938 | int match; | ||
939 | |||
940 | /* Check if client is in applicable range for ICMP NAT traversal | ||
941 | for this local address */ | ||
942 | if (! ch->natted_address) | ||
943 | continue; | ||
944 | match = GNUNET_NO; | ||
945 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
946 | { | ||
947 | struct ClientAddress *ca = &ch->caddrs[i]; | ||
948 | const struct sockaddr_in *c4; | ||
949 | |||
950 | if (AF_INET != ca->ss.ss_family) | ||
951 | continue; | ||
952 | c4 = (const struct sockaddr_in *) &ca->ss; | ||
953 | if ((0 != c4->sin_addr.s_addr) && | ||
954 | (l4->sin_addr.s_addr != c4->sin_addr.s_addr)) | ||
955 | continue; | ||
956 | match = GNUNET_YES; | ||
957 | break; | ||
958 | } | ||
959 | if (! match) | ||
960 | continue; | ||
961 | |||
962 | /* Notify applicable client about connection reversal request */ | ||
963 | env = GNUNET_MQ_msg_extra (crrm, | ||
964 | sizeof(struct sockaddr_in), | ||
965 | GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED); | ||
966 | GNUNET_memcpy (&crrm[1], | ||
967 | ra, | ||
968 | sizeof(struct sockaddr_in)); | ||
969 | GNUNET_MQ_send (ch->mq, | ||
970 | env); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | |||
975 | /** | ||
976 | * Task we run periodically to scan for network interfaces. | ||
977 | * | ||
978 | * @param cls NULL | ||
979 | */ | ||
980 | static void | ||
981 | run_scan (void *cls) | ||
982 | { | ||
983 | struct IfcProcContext ifc_ctx; | ||
984 | int found; | ||
985 | int have_nat; | ||
986 | struct LocalAddressList *lnext; | ||
987 | |||
988 | scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, | ||
989 | &run_scan, | ||
990 | NULL); | ||
991 | memset (&ifc_ctx, | ||
992 | 0, | ||
993 | sizeof(ifc_ctx)); | ||
994 | GNUNET_OS_network_interfaces_list (&ifc_proc, | ||
995 | &ifc_ctx); | ||
996 | /* remove addresses that disappeared */ | ||
997 | for (struct LocalAddressList *lal = lal_head; | ||
998 | NULL != lal; | ||
999 | lal = lnext) | ||
1000 | { | ||
1001 | lnext = lal->next; | ||
1002 | found = GNUNET_NO; | ||
1003 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | ||
1004 | NULL != pos; | ||
1005 | pos = pos->next) | ||
1006 | { | ||
1007 | if ((pos->af == lal->af) && | ||
1008 | (0 == memcmp (&lal->addr, | ||
1009 | &pos->addr, | ||
1010 | (AF_INET == lal->af) | ||
1011 | ? sizeof(struct sockaddr_in) | ||
1012 | : sizeof(struct sockaddr_in6)))) | ||
1013 | { | ||
1014 | found = GNUNET_YES; | ||
1015 | } | ||
1016 | } | ||
1017 | if (GNUNET_NO == found) | ||
1018 | { | ||
1019 | notify_clients (lal, | ||
1020 | GNUNET_NO); | ||
1021 | free_lal (lal); | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1025 | /* add addresses that appeared */ | ||
1026 | have_nat = GNUNET_NO; | ||
1027 | for (struct LocalAddressList *pos = ifc_ctx.lal_head; | ||
1028 | NULL != pos; | ||
1029 | pos = ifc_ctx.lal_head) | ||
1030 | { | ||
1031 | found = GNUNET_NO; | ||
1032 | if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac)) | ||
1033 | have_nat = GNUNET_YES; | ||
1034 | for (struct LocalAddressList *lal = lal_head; | ||
1035 | NULL != lal; | ||
1036 | lal = lal->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 | GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head, | ||
1047 | ifc_ctx.lal_tail, | ||
1048 | pos); | ||
1049 | if (GNUNET_YES == found) | ||
1050 | { | ||
1051 | GNUNET_free (pos); | ||
1052 | } | ||
1053 | else | ||
1054 | { | ||
1055 | notify_clients (pos, | ||
1056 | GNUNET_YES); | ||
1057 | GNUNET_CONTAINER_DLL_insert (lal_head, | ||
1058 | lal_tail, | ||
1059 | pos); | ||
1060 | if ((AF_INET == pos->af) && | ||
1061 | (NULL == pos->hc) && | ||
1062 | (0 != (GNUNET_NAT_AC_LAN & pos->ac))) | ||
1063 | { | ||
1064 | const struct sockaddr_in *s4 | ||
1065 | = (const struct sockaddr_in *) &pos->addr; | ||
1066 | |||
1067 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1068 | "Found NATed local address %s, starting NAT server\n", | ||
1069 | GNUNET_a2s ((const struct sockaddr *) &pos->addr, | ||
1070 | sizeof(*s4))); | ||
1071 | pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr, | ||
1072 | &reversal_callback, | ||
1073 | pos, | ||
1074 | cfg); | ||
1075 | } | ||
1076 | } | ||
1077 | } | ||
1078 | GN_nat_status_changed (have_nat); | ||
1079 | } | ||
1080 | |||
1081 | |||
1082 | /** | ||
1083 | * Function called whenever our set of external addresses | ||
1084 | * as created by `upnpc` changes. | ||
1085 | * | ||
1086 | * @param cls closure with our `struct ClientHandle *` | ||
1087 | * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean | ||
1088 | * the previous (now invalid) one, #GNUNET_SYSERR indicates an error | ||
1089 | * @param addr either the previous or the new public IP address | ||
1090 | * @param addrlen actual length of the @a addr | ||
1091 | * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code | ||
1092 | */ | ||
1093 | static void | ||
1094 | upnp_addr_change_cb (void *cls, | ||
1095 | int add_remove, | ||
1096 | const struct sockaddr *addr, | ||
1097 | socklen_t addrlen, | ||
1098 | enum GNUNET_NAT_StatusCode result) | ||
1099 | { | ||
1100 | struct ClientHandle *ch = cls; | ||
1101 | enum GNUNET_NAT_AddressClass ac; | ||
1102 | |||
1103 | switch (result) | ||
1104 | { | ||
1105 | case GNUNET_NAT_ERROR_SUCCESS: | ||
1106 | GNUNET_assert (NULL != addr); | ||
1107 | break; | ||
1108 | |||
1109 | case GNUNET_NAT_ERROR_UPNPC_FAILED: | ||
1110 | case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: | ||
1111 | case GNUNET_NAT_ERROR_IPC_FAILURE: | ||
1112 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1113 | "Running upnpc failed: %d\n", | ||
1114 | result); | ||
1115 | return; | ||
1116 | |||
1117 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: | ||
1118 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1119 | "external-ip binary not found\n"); | ||
1120 | return; | ||
1121 | |||
1122 | case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: | ||
1123 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1124 | "upnpc binary not found\n"); | ||
1125 | return; | ||
1126 | |||
1127 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: | ||
1128 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1129 | "external-ip binary could not be run\n"); | ||
1130 | return; | ||
1131 | |||
1132 | case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: | ||
1133 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1134 | "upnpc failed to create port mapping\n"); | ||
1135 | return; | ||
1136 | |||
1137 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: | ||
1138 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1139 | "Invalid output from upnpc\n"); | ||
1140 | return; | ||
1141 | |||
1142 | case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: | ||
1143 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1144 | "Invalid address returned by upnpc\n"); | ||
1145 | return; | ||
1146 | |||
1147 | default: | ||
1148 | GNUNET_break (0); /* should not be possible */ | ||
1149 | return; | ||
1150 | } | ||
1151 | switch (addr->sa_family) | ||
1152 | { | ||
1153 | case AF_INET: | ||
1154 | ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr) | ||
1155 | ? GNUNET_NAT_AC_LAN | ||
1156 | : GNUNET_NAT_AC_EXTERN; | ||
1157 | break; | ||
1158 | |||
1159 | case AF_INET6: | ||
1160 | ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr) | ||
1161 | ? GNUNET_NAT_AC_LAN | ||
1162 | : GNUNET_NAT_AC_EXTERN; | ||
1163 | break; | ||
1164 | |||
1165 | default: | ||
1166 | GNUNET_break (0); | ||
1167 | return; | ||
1168 | } | ||
1169 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1170 | "upnp external address %s: %s\n", | ||
1171 | add_remove ? "added" : "removed", | ||
1172 | GNUNET_a2s (addr, | ||
1173 | addrlen)); | ||
1174 | notify_client (ac, | ||
1175 | ch, | ||
1176 | add_remove, | ||
1177 | addr, | ||
1178 | addrlen); | ||
1179 | } | ||
1180 | |||
1181 | |||
1182 | /** | ||
1183 | * Resolve the `hole_external` name to figure out our | ||
1184 | * external address from a manually punched hole. The | ||
1185 | * port number has already been parsed, this task is | ||
1186 | * responsible for periodically doing a DNS lookup. | ||
1187 | * | ||
1188 | * @param ch client handle to act upon | ||
1189 | */ | ||
1190 | static void | ||
1191 | dyndns_lookup (void *cls); | ||
1192 | |||
1193 | |||
1194 | /** | ||
1195 | * Our (external) hostname was resolved. Update lists of | ||
1196 | * current external IPs (note that DNS may return multiple | ||
1197 | * addresses!) and notify client accordingly. | ||
1198 | * | ||
1199 | * @param cls the `struct ClientHandle` | ||
1200 | * @param addr NULL on error, otherwise result of DNS lookup | ||
1201 | * @param addrlen number of bytes in @a addr | ||
1202 | */ | ||
1203 | static void | ||
1204 | process_external_ip (void *cls, | ||
1205 | const struct sockaddr *addr, | ||
1206 | socklen_t addrlen) | ||
1207 | { | ||
1208 | struct ClientHandle *ch = cls; | ||
1209 | struct LocalAddressList *lal; | ||
1210 | struct sockaddr_storage ss; | ||
1211 | struct sockaddr_in *v4; | ||
1212 | struct sockaddr_in6 *v6; | ||
1213 | |||
1214 | if (NULL == addr) | ||
1215 | { | ||
1216 | struct LocalAddressList *laln; | ||
1217 | |||
1218 | ch->ext_dns = NULL; | ||
1219 | ch->ext_dns_task | ||
1220 | = GNUNET_SCHEDULER_add_delayed (dyndns_frequency, | ||
1221 | &dyndns_lookup, | ||
1222 | ch); | ||
1223 | /* Current iteration is over, remove 'old' IPs now */ | ||
1224 | for (lal = ch->ext_addr_head; NULL != lal; lal = laln) | ||
1225 | { | ||
1226 | laln = lal->next; | ||
1227 | if (GNUNET_YES == lal->old) | ||
1228 | { | ||
1229 | GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, | ||
1230 | ch->ext_addr_tail, | ||
1231 | lal); | ||
1232 | check_notify_client (lal, | ||
1233 | ch, | ||
1234 | GNUNET_NO); | ||
1235 | GNUNET_free (lal); | ||
1236 | } | ||
1237 | } | ||
1238 | return; | ||
1239 | } | ||
1240 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1241 | "Got IP `%s' for external address `%s'\n", | ||
1242 | GNUNET_a2s (addr, | ||
1243 | addrlen), | ||
1244 | ch->hole_external); | ||
1245 | |||
1246 | /* build sockaddr storage with port number */ | ||
1247 | memset (&ss, | ||
1248 | 0, | ||
1249 | sizeof(ss)); | ||
1250 | GNUNET_memcpy (&ss, | ||
1251 | addr, | ||
1252 | addrlen); | ||
1253 | switch (addr->sa_family) | ||
1254 | { | ||
1255 | case AF_INET: | ||
1256 | v4 = (struct sockaddr_in *) &ss; | ||
1257 | v4->sin_port = htons (ch->ext_dns_port); | ||
1258 | break; | ||
1259 | |||
1260 | case AF_INET6: | ||
1261 | v6 = (struct sockaddr_in6 *) &ss; | ||
1262 | v6->sin6_port = htons (ch->ext_dns_port); | ||
1263 | break; | ||
1264 | |||
1265 | default: | ||
1266 | GNUNET_break (0); | ||
1267 | return; | ||
1268 | } | ||
1269 | /* See if 'ss' matches any of our known addresses */ | ||
1270 | for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) | ||
1271 | { | ||
1272 | if (GNUNET_NO == lal->old) | ||
1273 | continue; /* already processed, skip */ | ||
1274 | if ((addr->sa_family == lal->addr.ss_family) && | ||
1275 | (0 == memcmp (&ss, | ||
1276 | &lal->addr, | ||
1277 | addrlen))) | ||
1278 | { | ||
1279 | /* Address unchanged, remember so we do not remove */ | ||
1280 | lal->old = GNUNET_NO; | ||
1281 | return; /* done here */ | ||
1282 | } | ||
1283 | } | ||
1284 | /* notify client, and remember IP for later removal! */ | ||
1285 | lal = GNUNET_new (struct LocalAddressList); | ||
1286 | lal->addr = ss; | ||
1287 | lal->af = ss.ss_family; | ||
1288 | lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; | ||
1289 | GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, | ||
1290 | ch->ext_addr_tail, | ||
1291 | lal); | ||
1292 | check_notify_client (lal, | ||
1293 | ch, | ||
1294 | GNUNET_YES); | ||
1295 | } | ||
1296 | |||
1297 | |||
1298 | /** | ||
1299 | * Resolve the `hole_external` name to figure out our | ||
1300 | * external address from a manually punched hole. The | ||
1301 | * port number has already been parsed, this task is | ||
1302 | * responsible for periodically doing a DNS lookup. | ||
1303 | * | ||
1304 | * @param ch client handle to act upon | ||
1305 | */ | ||
1306 | static void | ||
1307 | dyndns_lookup (void *cls) | ||
1308 | { | ||
1309 | struct ClientHandle *ch = cls; | ||
1310 | struct LocalAddressList *lal; | ||
1311 | |||
1312 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1313 | "Performing DNS lookup for punched hole given for `%s' as `%s:%u'\n", | ||
1314 | ch->section_name, | ||
1315 | ch->hole_external, | ||
1316 | (unsigned int) ch->ext_dns_port); | ||
1317 | for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) | ||
1318 | lal->old = GNUNET_YES; | ||
1319 | ch->ext_dns_task = NULL; | ||
1320 | ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external, | ||
1321 | AF_UNSPEC, | ||
1322 | GNUNET_TIME_UNIT_MINUTES, | ||
1323 | &process_external_ip, | ||
1324 | ch); | ||
1325 | } | ||
1326 | |||
1327 | |||
1328 | /** | ||
1329 | * Resolve the `hole_external` name to figure out our | ||
1330 | * external address from a manually punched hole. The | ||
1331 | * given name may be "AUTO" in which case we should use | ||
1332 | * the IP address(es) we have from upnpc or other methods. | ||
1333 | * The name can also be an IP address, in which case we | ||
1334 | * do not need to do DNS resolution. Finally, we also | ||
1335 | * need to parse the port number. | ||
1336 | * | ||
1337 | * @param ch client handle to act upon | ||
1338 | */ | ||
1339 | static void | ||
1340 | lookup_hole_external (struct ClientHandle *ch) | ||
1341 | { | ||
1342 | char *port; | ||
1343 | unsigned int pnum; | ||
1344 | struct sockaddr_in *s4; | ||
1345 | struct LocalAddressList *lal; | ||
1346 | |||
1347 | port = strrchr (ch->hole_external, ':'); | ||
1348 | if (NULL == port) | ||
1349 | { | ||
1350 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1351 | _ ("Malformed punched hole specification `%s' (lacks port)\n"), | ||
1352 | ch->hole_external); | ||
1353 | return; | ||
1354 | } | ||
1355 | if ((1 != sscanf (port + 1, | ||
1356 | "%u", | ||
1357 | &pnum)) || | ||
1358 | (pnum > 65535)) | ||
1359 | { | ||
1360 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1361 | _ ( | ||
1362 | "Invalid port number in punched hole specification `%s' (lacks port)\n"), | ||
1363 | port + 1); | ||
1364 | return; | ||
1365 | } | ||
1366 | ch->ext_dns_port = (uint16_t) pnum; | ||
1367 | *port = '\0'; | ||
1368 | |||
1369 | lal = GNUNET_new (struct LocalAddressList); | ||
1370 | if ('[' == *ch->hole_external) | ||
1371 | { | ||
1372 | struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr; | ||
1373 | |||
1374 | s6->sin6_family = AF_INET6; | ||
1375 | if (']' != (ch->hole_external[strlen (ch->hole_external) - 1])) | ||
1376 | { | ||
1377 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1378 | _ ("Malformed punched hole specification `%s' (lacks `]')\n"), | ||
1379 | ch->hole_external); | ||
1380 | GNUNET_free (lal); | ||
1381 | return; | ||
1382 | } | ||
1383 | ch->hole_external[strlen (ch->hole_external) - 1] = '\0'; | ||
1384 | if (1 != inet_pton (AF_INET6, | ||
1385 | ch->hole_external + 1, | ||
1386 | &s6->sin6_addr)) | ||
1387 | { | ||
1388 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1389 | _ ( | ||
1390 | "Malformed punched hole specification `%s' (IPv6 address invalid)"), | ||
1391 | ch->hole_external + 1); | ||
1392 | GNUNET_free (lal); | ||
1393 | return; | ||
1394 | } | ||
1395 | s6->sin6_port = htons (ch->ext_dns_port); | ||
1396 | lal->af = AF_INET6; | ||
1397 | lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; | ||
1398 | GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, | ||
1399 | ch->ext_addr_tail, | ||
1400 | lal); | ||
1401 | check_notify_client (lal, | ||
1402 | ch, | ||
1403 | GNUNET_YES); | ||
1404 | return; | ||
1405 | } | ||
1406 | |||
1407 | s4 = (struct sockaddr_in *) &lal->addr; | ||
1408 | s4->sin_family = AF_INET; | ||
1409 | if (1 == inet_pton (AF_INET, | ||
1410 | ch->hole_external, | ||
1411 | &s4->sin_addr)) | ||
1412 | { | ||
1413 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1414 | "IPv4 punched hole given for `%s' via `%s:%u'\n", | ||
1415 | ch->section_name, | ||
1416 | ch->hole_external, | ||
1417 | (unsigned int) ch->ext_dns_port); | ||
1418 | s4->sin_port = htons (ch->ext_dns_port); | ||
1419 | lal->af = AF_INET; | ||
1420 | lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; | ||
1421 | GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, | ||
1422 | ch->ext_addr_tail, | ||
1423 | lal); | ||
1424 | check_notify_client (lal, | ||
1425 | ch, | ||
1426 | GNUNET_YES); | ||
1427 | return; | ||
1428 | } | ||
1429 | if (0 == strcasecmp (ch->hole_external, | ||
1430 | "AUTO")) | ||
1431 | { | ||
1432 | /* handled in #notify_client_external_ipv4_change() */ | ||
1433 | GNUNET_free (lal); | ||
1434 | return; | ||
1435 | } | ||
1436 | /* got a DNS name, trigger lookup! */ | ||
1437 | GNUNET_free (lal); | ||
1438 | ch->ext_dns_task | ||
1439 | = GNUNET_SCHEDULER_add_now (&dyndns_lookup, | ||
1440 | ch); | ||
1441 | } | ||
1442 | |||
1443 | |||
1444 | /** | ||
1445 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. | ||
1446 | * We remember the client for updates upon future NAT events. | ||
1447 | * | ||
1448 | * @param cls client who sent the message | ||
1449 | * @param message the message received | ||
1450 | */ | ||
1451 | static void | ||
1452 | handle_register (void *cls, | ||
1453 | const struct GNUNET_NAT_RegisterMessage *message) | ||
1454 | { | ||
1455 | struct ClientHandle *ch = cls; | ||
1456 | const char *off; | ||
1457 | size_t left; | ||
1458 | |||
1459 | if ((0 != ch->proto) || | ||
1460 | (NULL != ch->caddrs)) | ||
1461 | { | ||
1462 | /* double registration not allowed */ | ||
1463 | GNUNET_break (0); | ||
1464 | GNUNET_SERVICE_client_drop (ch->client); | ||
1465 | return; | ||
1466 | } | ||
1467 | ch->flags = message->flags; | ||
1468 | ch->proto = message->proto; | ||
1469 | ch->num_caddrs = ntohs (message->num_addrs); | ||
1470 | ch->caddrs = GNUNET_new_array (ch->num_caddrs, | ||
1471 | struct ClientAddress); | ||
1472 | left = ntohs (message->header.size) - sizeof(*message); | ||
1473 | off = (const char *) &message[1]; | ||
1474 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
1475 | { | ||
1476 | const struct sockaddr *sa = (const struct sockaddr *) off; | ||
1477 | size_t alen; | ||
1478 | uint16_t port; | ||
1479 | int is_nat; | ||
1480 | |||
1481 | if (sizeof(sa_family_t) > left) | ||
1482 | { | ||
1483 | GNUNET_break (0); | ||
1484 | GNUNET_SERVICE_client_drop (ch->client); | ||
1485 | return; | ||
1486 | } | ||
1487 | is_nat = GNUNET_NO; | ||
1488 | switch (sa->sa_family) | ||
1489 | { | ||
1490 | case AF_INET: | ||
1491 | { | ||
1492 | struct sockaddr_in s4; | ||
1493 | |||
1494 | GNUNET_memcpy (&s4, | ||
1495 | off, | ||
1496 | sizeof(struct sockaddr_in)); | ||
1497 | alen = sizeof(struct sockaddr_in); | ||
1498 | if (is_nat_v4 (&s4.sin_addr)) | ||
1499 | is_nat = GNUNET_YES; | ||
1500 | port = ntohs (s4.sin_port); | ||
1501 | } | ||
1502 | break; | ||
1503 | |||
1504 | case AF_INET6: | ||
1505 | { | ||
1506 | struct sockaddr_in6 s6; | ||
1507 | |||
1508 | GNUNET_memcpy (&s6, | ||
1509 | off, | ||
1510 | sizeof(struct sockaddr_in6)); | ||
1511 | alen = sizeof(struct sockaddr_in6); | ||
1512 | if (is_nat_v6 (&s6.sin6_addr)) | ||
1513 | is_nat = GNUNET_YES; | ||
1514 | port = ntohs (s6.sin6_port); | ||
1515 | } | ||
1516 | break; | ||
1517 | |||
1518 | #if AF_UNIX | ||
1519 | case AF_UNIX: | ||
1520 | alen = sizeof(struct sockaddr_un); | ||
1521 | port = 0; | ||
1522 | break; | ||
1523 | #endif | ||
1524 | default: | ||
1525 | GNUNET_break (0); | ||
1526 | GNUNET_SERVICE_client_drop (ch->client); | ||
1527 | return; | ||
1528 | } | ||
1529 | /* store address */ | ||
1530 | GNUNET_assert (alen <= left); | ||
1531 | GNUNET_assert (alen <= sizeof(struct sockaddr_storage)); | ||
1532 | GNUNET_memcpy (&ch->caddrs[i].ss, | ||
1533 | off, | ||
1534 | alen); | ||
1535 | |||
1536 | /* If applicable, try UPNPC NAT punching */ | ||
1537 | if ((is_nat) && | ||
1538 | (enable_upnp) && | ||
1539 | ((IPPROTO_TCP == ch->proto) || | ||
1540 | (IPPROTO_UDP == ch->proto))) | ||
1541 | { | ||
1542 | ch->natted_address = GNUNET_YES; | ||
1543 | ch->caddrs[i].mh | ||
1544 | = GNUNET_NAT_mini_map_start (port, | ||
1545 | IPPROTO_TCP == ch->proto, | ||
1546 | &upnp_addr_change_cb, | ||
1547 | ch); | ||
1548 | } | ||
1549 | |||
1550 | off += alen; | ||
1551 | } | ||
1552 | |||
1553 | ch->section_name | ||
1554 | = GNUNET_strndup (off, | ||
1555 | ntohs (message->str_len)); | ||
1556 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1557 | "Received REGISTER message from client for subsystem `%s'\n", | ||
1558 | ch->section_name); | ||
1559 | if (GNUNET_OK == | ||
1560 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
1561 | ch->section_name, | ||
1562 | "HOLE_EXTERNAL", | ||
1563 | &ch->hole_external)) | ||
1564 | lookup_hole_external (ch); | ||
1565 | |||
1566 | /* Actually send IP address list to client */ | ||
1567 | for (struct LocalAddressList *lal = lal_head; | ||
1568 | NULL != lal; | ||
1569 | lal = lal->next) | ||
1570 | { | ||
1571 | check_notify_client (lal, | ||
1572 | ch, | ||
1573 | GNUNET_YES); | ||
1574 | } | ||
1575 | /* Also consider IPv4 determined by `external-ip` */ | ||
1576 | ch->external_monitor | ||
1577 | = GN_external_ipv4_monitor_start (¬ify_client_external_ipv4_change, | ||
1578 | ch); | ||
1579 | GNUNET_SERVICE_client_continue (ch->client); | ||
1580 | } | ||
1581 | |||
1582 | |||
1583 | /** | ||
1584 | * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from | ||
1585 | * client. | ||
1586 | * | ||
1587 | * @param cls client who sent the message | ||
1588 | * @param message the message received | ||
1589 | * @return #GNUNET_OK if message is well-formed | ||
1590 | */ | ||
1591 | static int | ||
1592 | check_stun (void *cls, | ||
1593 | const struct GNUNET_NAT_HandleStunMessage *message) | ||
1594 | { | ||
1595 | size_t sa_len = ntohs (message->sender_addr_size); | ||
1596 | size_t expect = sa_len + ntohs (message->payload_size); | ||
1597 | |||
1598 | if (ntohs (message->header.size) - sizeof(*message) != expect) | ||
1599 | { | ||
1600 | GNUNET_break (0); | ||
1601 | return GNUNET_SYSERR; | ||
1602 | } | ||
1603 | if (sa_len < sizeof(sa_family_t)) | ||
1604 | { | ||
1605 | GNUNET_break (0); | ||
1606 | return GNUNET_SYSERR; | ||
1607 | } | ||
1608 | return GNUNET_OK; | ||
1609 | } | ||
1610 | |||
1611 | |||
1612 | /** | ||
1613 | * Notify all clients about our external IP address | ||
1614 | * as reported by the STUN server. | ||
1615 | * | ||
1616 | * @param ip the external IP | ||
1617 | * @param add #GNUNET_YES to add, #GNUNET_NO to remove | ||
1618 | */ | ||
1619 | static void | ||
1620 | notify_clients_stun_change (const struct sockaddr_in *ip, | ||
1621 | int add) | ||
1622 | { | ||
1623 | for (struct ClientHandle *ch = ch_head; | ||
1624 | NULL != ch; | ||
1625 | ch = ch->next) | ||
1626 | { | ||
1627 | struct sockaddr_in v4; | ||
1628 | struct GNUNET_NAT_AddressChangeNotificationMessage *msg; | ||
1629 | struct GNUNET_MQ_Envelope *env; | ||
1630 | |||
1631 | if (! ch->natted_address) | ||
1632 | continue; | ||
1633 | v4 = *ip; | ||
1634 | v4.sin_port = htons (0); | ||
1635 | env = GNUNET_MQ_msg_extra (msg, | ||
1636 | sizeof(v4), | ||
1637 | GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); | ||
1638 | msg->add_remove = htonl ((int32_t) add); | ||
1639 | msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN | ||
1640 | | GNUNET_NAT_AC_GLOBAL); | ||
1641 | GNUNET_memcpy (&msg[1], | ||
1642 | &v4, | ||
1643 | sizeof(v4)); | ||
1644 | GNUNET_MQ_send (ch->mq, | ||
1645 | env); | ||
1646 | } | ||
1647 | } | ||
1648 | |||
1649 | |||
1650 | /** | ||
1651 | * Function to be called when we decide that an | ||
1652 | * external IP address as told to us by a STUN | ||
1653 | * server has gone stale. | ||
1654 | * | ||
1655 | * @param cls the `struct StunExternalIP` to drop | ||
1656 | */ | ||
1657 | static void | ||
1658 | stun_ip_timeout (void *cls) | ||
1659 | { | ||
1660 | struct StunExternalIP *se = cls; | ||
1661 | |||
1662 | se->timeout_task = NULL; | ||
1663 | notify_clients_stun_change (&se->external_addr, | ||
1664 | GNUNET_NO); | ||
1665 | GNUNET_CONTAINER_DLL_remove (se_head, | ||
1666 | se_tail, | ||
1667 | se); | ||
1668 | GNUNET_free (se); | ||
1669 | } | ||
1670 | |||
1671 | |||
1672 | /** | ||
1673 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from | ||
1674 | * client. | ||
1675 | * | ||
1676 | * @param cls client who sent the message | ||
1677 | * @param message the message received | ||
1678 | */ | ||
1679 | static void | ||
1680 | handle_stun (void *cls, | ||
1681 | const struct GNUNET_NAT_HandleStunMessage *message) | ||
1682 | { | ||
1683 | struct ClientHandle *ch = cls; | ||
1684 | const char *buf = (const char *) &message[1]; | ||
1685 | const struct sockaddr *sa; | ||
1686 | const void *payload; | ||
1687 | size_t sa_len; | ||
1688 | size_t payload_size; | ||
1689 | struct sockaddr_in external_addr; | ||
1690 | |||
1691 | sa_len = ntohs (message->sender_addr_size); | ||
1692 | payload_size = ntohs (message->payload_size); | ||
1693 | sa = (const struct sockaddr *) &buf[0]; | ||
1694 | payload = (const struct sockaddr *) &buf[sa_len]; | ||
1695 | switch (sa->sa_family) | ||
1696 | { | ||
1697 | case AF_INET: | ||
1698 | if (sa_len != sizeof(struct sockaddr_in)) | ||
1699 | { | ||
1700 | GNUNET_break (0); | ||
1701 | GNUNET_SERVICE_client_drop (ch->client); | ||
1702 | return; | ||
1703 | } | ||
1704 | break; | ||
1705 | |||
1706 | case AF_INET6: | ||
1707 | if (sa_len != sizeof(struct sockaddr_in6)) | ||
1708 | { | ||
1709 | GNUNET_break (0); | ||
1710 | GNUNET_SERVICE_client_drop (ch->client); | ||
1711 | return; | ||
1712 | } | ||
1713 | break; | ||
1714 | } | ||
1715 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1716 | "Received HANDLE_STUN message from client\n"); | ||
1717 | if (GNUNET_OK == | ||
1718 | GNUNET_NAT_stun_handle_packet_ (payload, | ||
1719 | payload_size, | ||
1720 | &external_addr)) | ||
1721 | { | ||
1722 | /* We now know that a server at "sa" claims that | ||
1723 | we are visible at IP "external_addr". | ||
1724 | |||
1725 | We should (for some fixed period of time) tell | ||
1726 | all of our clients that listen to a NAT'ed address | ||
1727 | that they might want to consider the given 'external_ip' | ||
1728 | as their public IP address (this includes TCP and UDP | ||
1729 | clients, even if only UDP sends STUN requests). | ||
1730 | |||
1731 | If we do not get a renewal, the "external_addr" should be | ||
1732 | removed again. The timeout frequency should be configurable | ||
1733 | (with a sane default), so that the UDP plugin can tell how | ||
1734 | often to re-request STUN. | ||
1735 | */struct StunExternalIP *se; | ||
1736 | |||
1737 | /* Check if we had a prior response from this STUN server */ | ||
1738 | for (se = se_head; NULL != se; se = se->next) | ||
1739 | { | ||
1740 | if ((se->stun_server_addr_len != sa_len) || | ||
1741 | (0 != memcmp (sa, | ||
1742 | &se->stun_server_addr, | ||
1743 | sa_len))) | ||
1744 | continue; /* different STUN server */ | ||
1745 | if (0 != GNUNET_memcmp (&external_addr, | ||
1746 | &se->external_addr)) | ||
1747 | { | ||
1748 | /* external IP changed, update! */ | ||
1749 | notify_clients_stun_change (&se->external_addr, | ||
1750 | GNUNET_NO); | ||
1751 | se->external_addr = external_addr; | ||
1752 | notify_clients_stun_change (&se->external_addr, | ||
1753 | GNUNET_YES); | ||
1754 | } | ||
1755 | /* update timeout */ | ||
1756 | GNUNET_SCHEDULER_cancel (se->timeout_task); | ||
1757 | se->timeout_task | ||
1758 | = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, | ||
1759 | &stun_ip_timeout, | ||
1760 | se); | ||
1761 | return; | ||
1762 | } | ||
1763 | /* STUN server is completely new, create fresh entry */ | ||
1764 | se = GNUNET_new (struct StunExternalIP); | ||
1765 | se->external_addr = external_addr; | ||
1766 | GNUNET_memcpy (&se->stun_server_addr, | ||
1767 | sa, | ||
1768 | sa_len); | ||
1769 | se->stun_server_addr_len = sa_len; | ||
1770 | se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, | ||
1771 | &stun_ip_timeout, | ||
1772 | se); | ||
1773 | GNUNET_CONTAINER_DLL_insert (se_head, | ||
1774 | se_tail, | ||
1775 | se); | ||
1776 | notify_clients_stun_change (&se->external_addr, | ||
1777 | GNUNET_NO); | ||
1778 | } | ||
1779 | GNUNET_SERVICE_client_continue (ch->client); | ||
1780 | } | ||
1781 | |||
1782 | |||
1783 | /** | ||
1784 | * Check validity of | ||
1785 | * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from | ||
1786 | * client. | ||
1787 | * | ||
1788 | * @param cls client who sent the message | ||
1789 | * @param message the message received | ||
1790 | * @return #GNUNET_OK if message is well-formed | ||
1791 | */ | ||
1792 | static int | ||
1793 | check_request_connection_reversal (void *cls, | ||
1794 | const struct | ||
1795 | GNUNET_NAT_RequestConnectionReversalMessage * | ||
1796 | message) | ||
1797 | { | ||
1798 | size_t expect; | ||
1799 | |||
1800 | expect = ntohs (message->local_addr_size) | ||
1801 | + ntohs (message->remote_addr_size); | ||
1802 | if (ntohs (message->header.size) - sizeof(*message) != expect) | ||
1803 | { | ||
1804 | GNUNET_break (0); | ||
1805 | return GNUNET_SYSERR; | ||
1806 | } | ||
1807 | return GNUNET_OK; | ||
1808 | } | ||
1809 | |||
1810 | |||
1811 | /** | ||
1812 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL | ||
1813 | * message from client. | ||
1814 | * | ||
1815 | * @param cls client who sent the message | ||
1816 | * @param message the message received | ||
1817 | */ | ||
1818 | static void | ||
1819 | handle_request_connection_reversal (void *cls, | ||
1820 | const struct | ||
1821 | GNUNET_NAT_RequestConnectionReversalMessage | ||
1822 | *message) | ||
1823 | { | ||
1824 | struct ClientHandle *ch = cls; | ||
1825 | const char *buf = (const char *) &message[1]; | ||
1826 | size_t local_sa_len = ntohs (message->local_addr_size); | ||
1827 | size_t remote_sa_len = ntohs (message->remote_addr_size); | ||
1828 | struct sockaddr_in l4; | ||
1829 | struct sockaddr_in r4; | ||
1830 | int ret; | ||
1831 | |||
1832 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1833 | "Received REQUEST CONNECTION REVERSAL message from client\n"); | ||
1834 | if (local_sa_len != sizeof(struct sockaddr_in)) | ||
1835 | { | ||
1836 | GNUNET_break_op (0); | ||
1837 | GNUNET_SERVICE_client_drop (ch->client); | ||
1838 | return; | ||
1839 | } | ||
1840 | if (remote_sa_len != sizeof(struct sockaddr_in)) | ||
1841 | { | ||
1842 | GNUNET_break_op (0); | ||
1843 | GNUNET_SERVICE_client_drop (ch->client); | ||
1844 | return; | ||
1845 | } | ||
1846 | GNUNET_memcpy (&l4, | ||
1847 | buf, | ||
1848 | sizeof(struct sockaddr_in)); | ||
1849 | GNUNET_break_op (AF_INET == l4.sin_family); | ||
1850 | buf += sizeof(struct sockaddr_in); | ||
1851 | GNUNET_memcpy (&r4, | ||
1852 | buf, | ||
1853 | sizeof(struct sockaddr_in)); | ||
1854 | GNUNET_break_op (AF_INET == r4.sin_family); | ||
1855 | ret = GN_request_connection_reversal (&l4.sin_addr, | ||
1856 | ntohs (l4.sin_port), | ||
1857 | &r4.sin_addr, | ||
1858 | cfg); | ||
1859 | if (GNUNET_OK != ret) | ||
1860 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1861 | _ ("Connection reversal request failed\n")); | ||
1862 | GNUNET_SERVICE_client_continue (ch->client); | ||
1863 | } | ||
1864 | |||
1865 | |||
1866 | /** | ||
1867 | * Task run during shutdown. | ||
1868 | * | ||
1869 | * @param cls unused | ||
1870 | */ | ||
1871 | static void | ||
1872 | shutdown_task (void *cls) | ||
1873 | { | ||
1874 | struct StunExternalIP *se; | ||
1875 | |||
1876 | while (NULL != (se = se_head)) | ||
1877 | { | ||
1878 | GNUNET_CONTAINER_DLL_remove (se_head, | ||
1879 | se_tail, | ||
1880 | se); | ||
1881 | GNUNET_SCHEDULER_cancel (se->timeout_task); | ||
1882 | GNUNET_free (se); | ||
1883 | } | ||
1884 | GN_nat_status_changed (GNUNET_NO); | ||
1885 | if (NULL != scan_task) | ||
1886 | { | ||
1887 | GNUNET_SCHEDULER_cancel (scan_task); | ||
1888 | scan_task = NULL; | ||
1889 | } | ||
1890 | if (NULL != stats) | ||
1891 | { | ||
1892 | GNUNET_STATISTICS_destroy (stats, | ||
1893 | GNUNET_NO); | ||
1894 | stats = NULL; | ||
1895 | } | ||
1896 | destroy_lal (); | ||
1897 | } | ||
1898 | |||
1899 | |||
1900 | /** | ||
1901 | * Setup NAT service. | ||
1902 | * | ||
1903 | * @param cls closure | ||
1904 | * @param c configuration to use | ||
1905 | * @param service the initialized service | ||
1906 | */ | ||
1907 | static void | ||
1908 | run (void *cls, | ||
1909 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
1910 | struct GNUNET_SERVICE_Handle *service) | ||
1911 | { | ||
1912 | cfg = c; | ||
1913 | if (GNUNET_OK != | ||
1914 | GNUNET_CONFIGURATION_get_value_time (cfg, | ||
1915 | "NAT", | ||
1916 | "STUN_STALE", | ||
1917 | &stun_stale_timeout)) | ||
1918 | stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; | ||
1919 | |||
1920 | /* Check for UPnP */ | ||
1921 | enable_upnp | ||
1922 | = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
1923 | "NAT", | ||
1924 | "ENABLE_UPNP"); | ||
1925 | if (GNUNET_YES == enable_upnp) | ||
1926 | { | ||
1927 | /* check if it works */ | ||
1928 | if (GNUNET_SYSERR == | ||
1929 | GNUNET_OS_check_helper_binary ("upnpc", | ||
1930 | GNUNET_NO, | ||
1931 | NULL)) | ||
1932 | { | ||
1933 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1934 | _ ( | ||
1935 | "UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n")); | ||
1936 | enable_upnp = GNUNET_SYSERR; | ||
1937 | } | ||
1938 | } | ||
1939 | if (GNUNET_OK != | ||
1940 | GNUNET_CONFIGURATION_get_value_time (cfg, | ||
1941 | "nat", | ||
1942 | "DYNDNS_FREQUENCY", | ||
1943 | &dyndns_frequency)) | ||
1944 | dyndns_frequency = DYNDNS_FREQUENCY; | ||
1945 | |||
1946 | enable_ipscan | ||
1947 | = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
1948 | "NAT", | ||
1949 | "ENABLE_IPSCAN"); | ||
1950 | |||
1951 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1952 | NULL); | ||
1953 | stats = GNUNET_STATISTICS_create ("nat", | ||
1954 | cfg); | ||
1955 | if (GNUNET_YES == enable_ipscan) | ||
1956 | scan_task = GNUNET_SCHEDULER_add_now (&run_scan, | ||
1957 | NULL); | ||
1958 | } | ||
1959 | |||
1960 | |||
1961 | /** | ||
1962 | * Callback called when a client connects to the service. | ||
1963 | * | ||
1964 | * @param cls closure for the service | ||
1965 | * @param c the new client that connected to the service | ||
1966 | * @param mq the message queue used to send messages to the client | ||
1967 | * @return a `struct ClientHandle` | ||
1968 | */ | ||
1969 | static void * | ||
1970 | client_connect_cb (void *cls, | ||
1971 | struct GNUNET_SERVICE_Client *c, | ||
1972 | struct GNUNET_MQ_Handle *mq) | ||
1973 | { | ||
1974 | struct ClientHandle *ch; | ||
1975 | |||
1976 | ch = GNUNET_new (struct ClientHandle); | ||
1977 | ch->mq = mq; | ||
1978 | ch->client = c; | ||
1979 | GNUNET_CONTAINER_DLL_insert (ch_head, | ||
1980 | ch_tail, | ||
1981 | ch); | ||
1982 | return ch; | ||
1983 | } | ||
1984 | |||
1985 | |||
1986 | /** | ||
1987 | * Callback called when a client disconnected from the service | ||
1988 | * | ||
1989 | * @param cls closure for the service | ||
1990 | * @param c the client that disconnected | ||
1991 | * @param internal_cls a `struct ClientHandle *` | ||
1992 | */ | ||
1993 | static void | ||
1994 | client_disconnect_cb (void *cls, | ||
1995 | struct GNUNET_SERVICE_Client *c, | ||
1996 | void *internal_cls) | ||
1997 | { | ||
1998 | struct ClientHandle *ch = internal_cls; | ||
1999 | struct LocalAddressList *lal; | ||
2000 | |||
2001 | GNUNET_CONTAINER_DLL_remove (ch_head, | ||
2002 | ch_tail, | ||
2003 | ch); | ||
2004 | for (unsigned int i = 0; i < ch->num_caddrs; i++) | ||
2005 | { | ||
2006 | if (NULL != ch->caddrs[i].mh) | ||
2007 | { | ||
2008 | GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh); | ||
2009 | ch->caddrs[i].mh = NULL; | ||
2010 | } | ||
2011 | } | ||
2012 | GNUNET_free (ch->caddrs); | ||
2013 | while (NULL != (lal = ch->ext_addr_head)) | ||
2014 | { | ||
2015 | GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, | ||
2016 | ch->ext_addr_tail, | ||
2017 | lal); | ||
2018 | GNUNET_free (lal); | ||
2019 | } | ||
2020 | if (NULL != ch->ext_dns_task) | ||
2021 | { | ||
2022 | GNUNET_SCHEDULER_cancel (ch->ext_dns_task); | ||
2023 | ch->ext_dns_task = NULL; | ||
2024 | } | ||
2025 | if (NULL != ch->external_monitor) | ||
2026 | { | ||
2027 | GN_external_ipv4_monitor_stop (ch->external_monitor); | ||
2028 | ch->external_monitor = NULL; | ||
2029 | } | ||
2030 | if (NULL != ch->ext_dns) | ||
2031 | { | ||
2032 | GNUNET_RESOLVER_request_cancel (ch->ext_dns); | ||
2033 | ch->ext_dns = NULL; | ||
2034 | } | ||
2035 | GNUNET_free (ch->hole_external); | ||
2036 | GNUNET_free (ch->section_name); | ||
2037 | GNUNET_free (ch); | ||
2038 | } | ||
2039 | |||
2040 | |||
2041 | /** | ||
2042 | * Define "main" method using service macro. | ||
2043 | */ | ||
2044 | GNUNET_SERVICE_MAIN | ||
2045 | ("nat", | ||
2046 | GNUNET_SERVICE_OPTION_NONE, | ||
2047 | &run, | ||
2048 | &client_connect_cb, | ||
2049 | &client_disconnect_cb, | ||
2050 | NULL, | ||
2051 | GNUNET_MQ_hd_var_size (register, | ||
2052 | GNUNET_MESSAGE_TYPE_NAT_REGISTER, | ||
2053 | struct GNUNET_NAT_RegisterMessage, | ||
2054 | NULL), | ||
2055 | GNUNET_MQ_hd_var_size (stun, | ||
2056 | GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN, | ||
2057 | struct GNUNET_NAT_HandleStunMessage, | ||
2058 | NULL), | ||
2059 | GNUNET_MQ_hd_var_size (request_connection_reversal, | ||
2060 | GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL, | ||
2061 | struct GNUNET_NAT_RequestConnectionReversalMessage, | ||
2062 | NULL), | ||
2063 | GNUNET_MQ_handler_end ()); | ||
2064 | |||
2065 | |||
2066 | #if defined(__linux__) && defined(__GLIBC__) | ||
2067 | #include <malloc.h> | ||
2068 | |||
2069 | /** | ||
2070 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
2071 | */ | ||
2072 | void __attribute__ ((constructor)) | ||
2073 | GNUNET_ARM_memory_init () | ||
2074 | { | ||
2075 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
2076 | mallopt (M_TOP_PAD, 1 * 1024); | ||
2077 | malloc_trim (0); | ||
2078 | } | ||
2079 | |||
2080 | |||
2081 | #endif | ||
2082 | |||
2083 | /* end of gnunet-service-nat.c */ | ||