aboutsummaryrefslogtreecommitdiff
path: root/src/nat
diff options
context:
space:
mode:
Diffstat (limited to 'src/nat')
-rw-r--r--src/nat/.gitignore5
-rw-r--r--src/nat/Makefile.am118
-rw-r--r--src/nat/gnunet-helper-nat-client.c546
-rw-r--r--src/nat/gnunet-helper-nat-server.c714
-rwxr-xr-xsrc/nat/gnunet-nat-client-script.sh4
-rwxr-xr-xsrc/nat/gnunet-nat-server-script.sh4
-rw-r--r--src/nat/gnunet-nat.c476
-rw-r--r--src/nat/gnunet-service-nat.c2083
-rw-r--r--src/nat/gnunet-service-nat.h35
-rw-r--r--src/nat/gnunet-service-nat_externalip.c316
-rw-r--r--src/nat/gnunet-service-nat_externalip.h92
-rw-r--r--src/nat/gnunet-service-nat_helper.c401
-rw-r--r--src/nat/gnunet-service-nat_helper.h95
-rw-r--r--src/nat/gnunet-service-nat_mini.c707
-rw-r--r--src/nat/gnunet-service-nat_mini.h128
-rw-r--r--src/nat/gnunet-service-nat_stun.c215
-rw-r--r--src/nat/gnunet-service-nat_stun.h61
-rw-r--r--src/nat/nat.conf.in43
-rw-r--r--src/nat/nat.h223
-rw-r--r--src/nat/nat_api.c718
-rw-r--r--src/nat/nat_api_stun.c259
-rw-r--r--src/nat/nat_stun.h267
-rw-r--r--src/nat/test_nat.c192
-rw-r--r--src/nat/test_nat_data.conf36
-rw-r--r--src/nat/test_nat_mini.c134
-rw-r--r--src/nat/test_nat_test.c142
-rw-r--r--src/nat/test_nat_test_data.conf45
-rw-r--r--src/nat/test_stun.c313
-rw-r--r--src/nat/test_stun.conf7
29 files changed, 0 insertions, 8379 deletions
diff --git a/src/nat/.gitignore b/src/nat/.gitignore
deleted file mode 100644
index 868abab4b..000000000
--- a/src/nat/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
1gnunet-service-nat
2gnunet-helper-nat-client
3gnunet-helper-nat-server
4gnunet-nat
5gnunet-nat-server
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
deleted file mode 100644
index 3eea5e2a4..000000000
--- a/src/nat/Makefile.am
+++ /dev/null
@@ -1,118 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4libexecdir= $(pkglibdir)/libexec/
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8pkgcfg_DATA = \
9 nat.conf
10
11if LINUX
12 NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client
13 NATSERVER = gnunet-helper-nat-server.c
14 NATCLIENT = gnunet-helper-nat-client.c
15else
16if XFREEBSD
17 NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client
18 NATSERVER = gnunet-helper-nat-server.c
19 NATCLIENT = gnunet-helper-nat-client.c
20endif
21else
22install-exec-hook:
23endif
24
25bin_PROGRAMS = \
26 gnunet-nat
27
28libexec_PROGRAMS = \
29 $(NATBIN) \
30 gnunet-service-nat
31
32
33gnunet_helper_nat_server_SOURCES = \
34 $(NATSERVER)
35
36gnunet_helper_nat_client_SOURCES = \
37 $(NATCLIENT)
38
39
40gnunet_nat_SOURCES = \
41 gnunet-nat.c nat.h
42gnunet_nat_LDADD = \
43 libgnunetnatnew.la \
44 $(top_builddir)/src/util/libgnunetutil.la
45gnunet_nat_LDFLAGS = \
46 $(GN_LIBINTL)
47
48
49if USE_COVERAGE
50 AM_CFLAGS = -fprofile-arcs -ftest-coverage
51endif
52
53lib_LTLIBRARIES = \
54 libgnunetnatnew.la
55
56libgnunetnatnew_la_SOURCES = \
57 nat_api.c \
58 nat_api_stun.c nat_stun.h \
59 nat.h
60libgnunetnatnew_la_LIBADD = \
61 $(top_builddir)/src/util/libgnunetutil.la \
62 $(GN_LIBINTL) @EXT_LIBS@
63libgnunetnatnew_la_LDFLAGS = \
64 $(GN_LIB_LDFLAGS) \
65 -version-info 2:0:0
66
67gnunet_service_nat_SOURCES = \
68 gnunet-service-nat.c gnunet-service-nat.h \
69 gnunet-service-nat_externalip.c gnunet-service-nat_externalip.h \
70 gnunet-service-nat_stun.c gnunet-service-nat_stun.h \
71 gnunet-service-nat_mini.c gnunet-service-nat_mini.h \
72 gnunet-service-nat_helper.c gnunet-service-nat_helper.h
73gnunet_service_nat_LDADD = \
74 $(top_builddir)/src/util/libgnunetutil.la \
75 $(top_builddir)/src/statistics/libgnunetstatistics.la \
76 $(LIBGCRYPT_LIBS) \
77 -lgcrypt \
78 $(GN_LIBINTL)
79
80#check_PROGRAMS = \
81# test_nat \
82# test_nat_mini \
83# test_nat_test \
84# test_stun
85
86if ENABLE_TEST_RUN
87 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
88 TESTS = $(check_PROGRAMS)
89endif
90
91#test_nat_SOURCES = \
92# test_nat.c
93#test_nat_LDADD = \
94# libgnunetnat.la \
95# $(top_builddir)/src/util/libgnunetutil.la
96
97#test_nat_mini_SOURCES = \
98# test_nat_mini.c
99#test_nat_mini_LDADD = \
100# libgnunetnat.la \
101# $(top_builddir)/src/util/libgnunetutil.la
102
103#test_nat_test_SOURCES = \
104# test_nat_test.c
105#test_nat_test_LDADD = \
106# libgnunetnat.la \
107# $(top_builddir)/src/util/libgnunetutil.la
108
109#test_stun_SOURCES = \
110# test_stun.c
111#test_stun_LDADD = \
112# libgnunetnat.la \
113# $(top_builddir)/src/util/libgnunetutil.la
114
115EXTRA_DIST = \
116 test_nat_data.conf \
117 test_nat_test_data.conf \
118 test_stun.conf
diff --git a/src/nat/gnunet-helper-nat-client.c b/src/nat/gnunet-helper-nat-client.c
deleted file mode 100644
index 0271d6e0f..000000000
--- a/src/nat/gnunet-helper-nat-client.c
+++ /dev/null
@@ -1,546 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010 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 src/nat/gnunet-helper-nat-client.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only.
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message using RAW sockets
28 * to the IP address specified as the second argument. Since
29 * it uses RAW sockets, it must be installed SUID or run as 'root'.
30 * In order to keep the security risk of the resulting SUID binary
31 * minimal, the program ONLY opens the RAW socket with root
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Christian Grothoff
41 * - Nathan Evans
42 * - Benjamin Kuperman (22 Aug 2010)
43 */
44#if HAVE_CONFIG_H
45/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46#include "gnunet_config.h"
47#else
48#define _GNU_SOURCE
49#endif
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <arpa/inet.h>
53#include <sys/types.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <string.h>
57#include <errno.h>
58#include <stdlib.h>
59#include <stdint.h>
60#include <netinet/ip.h>
61#include <netinet/ip_icmp.h>
62#include <netinet/in.h>
63
64/* The following constant is missing from FreeBSD 9.2 */
65#ifndef ICMP_TIME_EXCEEDED
66#define ICMP_TIME_EXCEEDED 11
67#endif
68
69/**
70 * Call memcpy() but check for @a n being 0 first. In the latter
71 * case, it is now safe to pass NULL for @a src or @a dst.
72 * Unlike traditional memcpy(), returns nothing.
73 *
74 * @param dst destination of the copy, may be NULL if @a n is zero
75 * @param src source of the copy, may be NULL if @a n is zero
76 * @param n number of bytes to copy
77 */
78#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \
79 n); \
80 } } while (0)
81
82/**
83 * Must match IP given in the server.
84 */
85#define DUMMY_IP "192.0.2.86"
86
87#define NAT_TRAV_PORT 22225
88
89/**
90 * Must match packet ID used by gnunet-helper-nat-server.c
91 */
92#define PACKET_ID 256
93
94/**
95 * IPv4 header.
96 */
97struct ip_header
98{
99 /**
100 * Version (4 bits) + Internet header length (4 bits)
101 */
102 uint8_t vers_ihl;
103
104 /**
105 * Type of service
106 */
107 uint8_t tos;
108
109 /**
110 * Total length
111 */
112 uint16_t pkt_len;
113
114 /**
115 * Identification
116 */
117 uint16_t id;
118
119 /**
120 * Flags (3 bits) + Fragment offset (13 bits)
121 */
122 uint16_t flags_frag_offset;
123
124 /**
125 * Time to live
126 */
127 uint8_t ttl;
128
129 /**
130 * Protocol
131 */
132 uint8_t proto;
133
134 /**
135 * Header checksum
136 */
137 uint16_t checksum;
138
139 /**
140 * Source address
141 */
142 uint32_t src_ip;
143
144 /**
145 * Destination address
146 */
147 uint32_t dst_ip;
148};
149
150/**
151 * Format of ICMP packet.
152 */
153struct icmp_ttl_exceeded_header
154{
155 uint8_t type;
156
157 uint8_t code;
158
159 uint16_t checksum;
160
161 uint32_t unused;
162
163 /* followed by original payload */
164};
165
166struct icmp_echo_header
167{
168 uint8_t type;
169
170 uint8_t code;
171
172 uint16_t checksum;
173
174 uint32_t reserved;
175};
176
177/**
178 * Beginning of UDP packet.
179 */
180struct udp_header
181{
182 uint16_t src_port;
183
184 uint16_t dst_port;
185
186 uint16_t length;
187
188 uint16_t crc;
189};
190
191/**
192 * Socket we use to send our fake ICMP replies.
193 */
194static int rawsock;
195
196/**
197 * Target "dummy" address of the packet we pretend to respond to.
198 */
199static struct in_addr dummy;
200
201/**
202 * Our "source" port.
203 */
204static uint16_t port;
205
206
207/**
208 * CRC-16 for IP/ICMP headers.
209 *
210 * @param data what to calculate the CRC over
211 * @param bytes number of bytes in data (must be multiple of 2)
212 * @return the CRC 16.
213 */
214static uint16_t
215calc_checksum (const uint16_t *data, unsigned int bytes)
216{
217 uint32_t sum;
218 unsigned int i;
219
220 sum = 0;
221 for (i = 0; i < bytes / 2; i++)
222 sum += data[i];
223 sum = (sum & 0xffff) + (sum >> 16);
224 sum = htons (0xffff - sum);
225 return sum;
226}
227
228
229/**
230 * Send an ICMP message to the target.
231 *
232 * @param my_ip source address
233 * @param other target address
234 */
235static void
236send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
237{
238 char packet[sizeof(struct ip_header) * 2
239 + sizeof(struct icmp_ttl_exceeded_header)
240 + sizeof(struct udp_header)];
241 struct ip_header ip_pkt;
242 struct icmp_ttl_exceeded_header icmp_pkt;
243 struct udp_header udp_pkt;
244 struct sockaddr_in dst;
245 size_t off;
246 int err;
247
248 /* ip header: send to (known) ip address */
249 off = 0;
250 ip_pkt.vers_ihl = 0x45;
251 ip_pkt.tos = 0;
252 /* should this be BSD only? */
253#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__)
254 ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
255#else
256 ip_pkt.pkt_len = htons (sizeof(packet));
257#endif
258 ip_pkt.id = htons (PACKET_ID);
259 ip_pkt.flags_frag_offset = 0;
260 ip_pkt.ttl = 128;
261 ip_pkt.proto = IPPROTO_ICMP;
262 ip_pkt.checksum = 0;
263 ip_pkt.src_ip = my_ip->s_addr;
264 ip_pkt.dst_ip = other->s_addr;
265 ip_pkt.checksum =
266 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
267 GNUNET_memcpy (&packet[off],
268 &ip_pkt,
269 sizeof(struct ip_header));
270 off += sizeof(struct ip_header);
271
272 icmp_pkt.type = ICMP_TIME_EXCEEDED;
273 icmp_pkt.code = 0;
274 icmp_pkt.checksum = 0;
275 icmp_pkt.unused = 0;
276 GNUNET_memcpy (&packet[off],
277 &icmp_pkt,
278 sizeof(struct icmp_ttl_exceeded_header));
279 off += sizeof(struct icmp_ttl_exceeded_header);
280
281 /* ip header of the presumably 'lost' udp packet */
282 ip_pkt.vers_ihl = 0x45;
283 ip_pkt.tos = 0;
284 ip_pkt.pkt_len =
285 htons (sizeof(struct ip_header) + sizeof(struct udp_header));
286 ip_pkt.id = htons (0);
287 ip_pkt.flags_frag_offset = 0;
288 ip_pkt.ttl = 128;
289 ip_pkt.proto = IPPROTO_UDP;
290 ip_pkt.checksum = 0;
291 ip_pkt.src_ip = other->s_addr;
292 ip_pkt.dst_ip = dummy.s_addr;
293 ip_pkt.checksum =
294 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
295 GNUNET_memcpy (&packet[off],
296 &ip_pkt,
297 sizeof(struct ip_header));
298 off += sizeof(struct ip_header);
299
300 /* build UDP header */
301 udp_pkt.src_port = htons (NAT_TRAV_PORT);
302 udp_pkt.dst_port = htons (NAT_TRAV_PORT);
303 udp_pkt.length = htons (port);
304 udp_pkt.crc = 0;
305 GNUNET_memcpy (&packet[off],
306 &udp_pkt,
307 sizeof(struct udp_header));
308 off += sizeof(struct udp_header);
309
310 /* set ICMP checksum */
311 icmp_pkt.checksum =
312 htons (calc_checksum
313 ((uint16_t *) &packet[sizeof(struct ip_header)],
314 sizeof(struct icmp_ttl_exceeded_header)
315 + sizeof(struct ip_header) + sizeof(struct udp_header)));
316 GNUNET_memcpy (&packet[sizeof(struct ip_header)],
317 &icmp_pkt,
318 sizeof(struct icmp_ttl_exceeded_header));
319
320 memset (&dst, 0, sizeof(dst));
321 dst.sin_family = AF_INET;
322#if HAVE_SOCKADDR_IN_SIN_LEN
323 dst.sin_len = sizeof(struct sockaddr_in);
324#endif
325 dst.sin_addr = *other;
326 err =
327 sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
328 sizeof(dst));
329 if (err < 0)
330 {
331 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
332 }
333 else if (sizeof(packet) != (size_t) err)
334 {
335 fprintf (stderr, "Error: partial send of ICMP message with size %lu\n",
336 (unsigned long) off);
337 }
338}
339
340
341/**
342 * Send an ICMP message to the target.
343 *
344 * @param my_ip source address
345 * @param other target address
346 */
347static void
348send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
349{
350 struct ip_header ip_pkt;
351 struct icmp_ttl_exceeded_header icmp_ttl;
352 struct icmp_echo_header icmp_echo;
353 struct sockaddr_in dst;
354 char packet[sizeof(struct ip_header) * 2
355 + sizeof(struct icmp_ttl_exceeded_header)
356 + sizeof(struct icmp_echo_header)];
357 size_t off;
358 int err;
359
360 /* ip header: send to (known) ip address */
361 off = 0;
362 ip_pkt.vers_ihl = 0x45;
363 ip_pkt.tos = 0;
364#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__)
365 ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
366#else
367 ip_pkt.pkt_len = htons (sizeof(packet));
368#endif
369 ip_pkt.id = htons (PACKET_ID);
370 ip_pkt.flags_frag_offset = 0;
371 ip_pkt.ttl = IPDEFTTL;
372 ip_pkt.proto = IPPROTO_ICMP;
373 ip_pkt.checksum = 0;
374 ip_pkt.src_ip = my_ip->s_addr;
375 ip_pkt.dst_ip = other->s_addr;
376 ip_pkt.checksum =
377 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
378 GNUNET_memcpy (&packet[off],
379 &ip_pkt,
380 sizeof(struct ip_header));
381 off = sizeof(ip_pkt);
382
383 /* icmp reply: time exceeded */
384 icmp_ttl.type = ICMP_TIME_EXCEEDED;
385 icmp_ttl.code = 0;
386 icmp_ttl.checksum = 0;
387 icmp_ttl.unused = 0;
388 GNUNET_memcpy (&packet[off],
389 &icmp_ttl,
390 sizeof(struct icmp_ttl_exceeded_header));
391 off += sizeof(struct icmp_ttl_exceeded_header);
392
393 /* ip header of the presumably 'lost' udp packet */
394 ip_pkt.vers_ihl = 0x45;
395 ip_pkt.tos = 0;
396 ip_pkt.pkt_len =
397 htons (sizeof(struct ip_header) + sizeof(struct icmp_echo_header));
398 ip_pkt.id = htons (PACKET_ID);
399 ip_pkt.flags_frag_offset = 0;
400 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
401 ip_pkt.proto = IPPROTO_ICMP;
402 ip_pkt.src_ip = other->s_addr;
403 ip_pkt.dst_ip = dummy.s_addr;
404 ip_pkt.checksum = 0;
405 ip_pkt.checksum =
406 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
407 GNUNET_memcpy (&packet[off],
408 &ip_pkt,
409 sizeof(struct ip_header));
410 off += sizeof(struct ip_header);
411
412 icmp_echo.type = ICMP_ECHO;
413 icmp_echo.code = 0;
414 icmp_echo.reserved = htonl (port);
415 icmp_echo.checksum = 0;
416 icmp_echo.checksum =
417 htons (calc_checksum
418 ((uint16_t *) &icmp_echo, sizeof(struct icmp_echo_header)));
419 GNUNET_memcpy (&packet[off],
420 &icmp_echo,
421 sizeof(struct icmp_echo_header));
422
423 /* no go back to calculate ICMP packet checksum */
424 off = sizeof(struct ip_header);
425 icmp_ttl.checksum =
426 htons (calc_checksum
427 ((uint16_t *) &packet[off],
428 sizeof(struct icmp_ttl_exceeded_header)
429 + sizeof(struct ip_header) + sizeof(struct icmp_echo_header)));
430 GNUNET_memcpy (&packet[off],
431 &icmp_ttl,
432 sizeof(struct icmp_ttl_exceeded_header));
433
434 /* prepare for transmission */
435 memset (&dst, 0, sizeof(dst));
436 dst.sin_family = AF_INET;
437#if HAVE_SOCKADDR_IN_SIN_LEN
438 dst.sin_len = sizeof(struct sockaddr_in);
439#endif
440 dst.sin_addr = *other;
441 err =
442 sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
443 sizeof(dst));
444 if (err < 0)
445 {
446 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
447 }
448 else if (sizeof(packet) != (size_t) err)
449 {
450 fprintf (stderr, "Error: partial send of ICMP message\n");
451 }
452}
453
454
455int
456main (int argc, char *const *argv)
457{
458 const int one = 1;
459 struct in_addr external;
460 struct in_addr target;
461 uid_t uid;
462 unsigned int p;
463 int raw_eno;
464 int global_ret;
465
466 /* Create an ICMP raw socket for writing (only operation that requires root) */
467 rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
468 raw_eno = errno; /* for later error checking */
469
470 /* now drop root privileges */
471 uid = getuid ();
472#ifdef HAVE_SETRESUID
473 if (0 != setresuid (uid, uid, uid))
474 {
475 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
476 global_ret = 1;
477 goto cleanup;
478 }
479#else
480 if (0 != (setuid (uid) | seteuid (uid)))
481 {
482 fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
483 global_ret = 2;
484 goto cleanup;
485 }
486#endif
487 if (-1 == rawsock)
488 {
489 fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
490 global_ret = 3;
491 goto cleanup;
492 }
493 if (0 !=
494 setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one,
495 sizeof(one)))
496 {
497 fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
498 global_ret = 4;
499 goto cleanup;
500 }
501 if (0 !=
502 setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one)))
503 {
504 fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
505 global_ret = 5;
506 goto cleanup;
507 }
508
509 if (4 != argc)
510 {
511 fprintf (stderr,
512 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
513 global_ret = 6;
514 goto cleanup;
515 }
516 if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
517 (1 != inet_pton (AF_INET, argv[2], &target)))
518 {
519 fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
520 global_ret = 7;
521 goto cleanup;
522 }
523 if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
524 {
525 fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
526 global_ret = 8;
527 goto cleanup;
528 }
529 port = (uint16_t) p;
530 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
531 {
532 fprintf (stderr, "Internal error converting dummy IP to binary.\n");
533 global_ret = 9;
534 goto cleanup;
535 }
536 send_icmp (&external, &target);
537 send_icmp_udp (&external, &target);
538 global_ret = 0;
539cleanup:
540 if (-1 != rawsock)
541 (void) close (rawsock);
542 return global_ret;
543}
544
545
546/* end of gnunet-helper-nat-client.c */
diff --git a/src/nat/gnunet-helper-nat-server.c b/src/nat/gnunet-helper-nat-server.c
deleted file mode 100644
index 38d7d22c8..000000000
--- a/src/nat/gnunet-helper-nat-server.c
+++ /dev/null
@@ -1,714 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010 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 src/nat/gnunet-helper-nat-server.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only (or maybe BSDs, but never W32)
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message every 500 ms RAW sockets
28 * to a DUMMY IP address and also listens for ICMP replies. Since
29 * it uses RAW sockets, it must be installed SUID or run as 'root'.
30 * In order to keep the security risk of the resulting SUID binary
31 * minimal, the program ONLY opens the two RAW sockets with root
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Christian Grothoff
41 * - Nathan Evans
42 * - Benjamin Kuperman (22 Aug 2010)
43 * - Jacob Appelbaum (19 Dec 2011)
44 */
45#if HAVE_CONFIG_H
46/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
47#include "gnunet_config.h"
48#else
49#define _GNU_SOURCE
50#endif
51#include <sys/types.h>
52#include <sys/socket.h>
53#include <arpa/inet.h>
54#include <sys/select.h>
55#include <sys/time.h>
56#include <sys/types.h>
57#include <unistd.h>
58#include <stdio.h>
59#include <string.h>
60#include <errno.h>
61#include <stdlib.h>
62#include <stdint.h>
63#include <time.h>
64#include <netinet/ip.h>
65#include <netinet/ip_icmp.h>
66#include <netinet/in.h>
67
68/* The following constant is missing from FreeBSD 9.2 */
69#ifndef ICMP_TIME_EXCEEDED
70#define ICMP_TIME_EXCEEDED 11
71#endif
72
73/**
74 * Call memcpy() but check for @a n being 0 first. In the latter
75 * case, it is now safe to pass NULL for @a src or @a dst.
76 * Unlike traditional memcpy(), returns nothing.
77 *
78 * @param dst destination of the copy, may be NULL if @a n is zero
79 * @param src source of the copy, may be NULL if @a n is zero
80 * @param n number of bytes to copy
81 */
82#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \
83 n); \
84 } } while (0)
85
86/**
87 * Should we print some debug output?
88 */
89#define VERBOSE 0
90
91/**
92 * Must match packet ID used by gnunet-helper-nat-client.c
93 */
94#define PACKET_ID 256
95
96/**
97 * Must match IP given in the client.
98 */
99#define DUMMY_IP "192.0.2.86"
100
101/**
102 * Port for UDP
103 */
104#define NAT_TRAV_PORT 22225
105
106/**
107 * How often do we send our ICMP messages to receive replies?
108 */
109#define ICMP_SEND_FREQUENCY_MS 500
110
111/**
112 * IPv4 header.
113 */
114struct ip_header
115{
116 /**
117 * Version (4 bits) + Internet header length (4 bits)
118 */
119 uint8_t vers_ihl;
120
121 /**
122 * Type of service
123 */
124 uint8_t tos;
125
126 /**
127 * Total length
128 */
129 uint16_t pkt_len;
130
131 /**
132 * Identification
133 */
134 uint16_t id;
135
136 /**
137 * Flags (3 bits) + Fragment offset (13 bits)
138 */
139 uint16_t flags_frag_offset;
140
141 /**
142 * Time to live
143 */
144 uint8_t ttl;
145
146 /**
147 * Protocol
148 */
149 uint8_t proto;
150
151 /**
152 * Header checksum
153 */
154 uint16_t checksum;
155
156 /**
157 * Source address
158 */
159 uint32_t src_ip;
160
161 /**
162 * Destination address
163 */
164 uint32_t dst_ip;
165};
166
167/**
168 * Format of ICMP packet.
169 */
170struct icmp_ttl_exceeded_header
171{
172 uint8_t type;
173
174 uint8_t code;
175
176 uint16_t checksum;
177
178 uint32_t unused;
179
180 /* followed by original payload */
181};
182
183struct icmp_echo_header
184{
185 uint8_t type;
186
187 uint8_t code;
188
189 uint16_t checksum;
190
191 uint32_t reserved;
192};
193
194
195/**
196 * Beginning of UDP packet.
197 */
198struct udp_header
199{
200 uint16_t src_port;
201
202 uint16_t dst_port;
203
204 uint16_t length;
205
206 uint16_t crc;
207};
208
209/**
210 * Socket we use to receive "fake" ICMP replies.
211 */
212static int icmpsock;
213
214/**
215 * Socket we use to send our ICMP requests.
216 */
217static int rawsock;
218
219/**
220 * Socket we use to send our UDP requests.
221 */
222static int udpsock;
223
224/**
225 * Target "dummy" address.
226 */
227static struct in_addr dummy;
228
229
230/**
231 * CRC-16 for IP/ICMP headers.
232 *
233 * @param data what to calculate the CRC over
234 * @param bytes number of bytes in data (must be multiple of 2)
235 * @return the CRC 16.
236 */
237static uint16_t
238calc_checksum (const uint16_t *data, unsigned int bytes)
239{
240 uint32_t sum;
241 unsigned int i;
242
243 sum = 0;
244 for (i = 0; i < bytes / 2; i++)
245 sum += data[i];
246 sum = (sum & 0xffff) + (sum >> 16);
247 sum = htons (0xffff - sum);
248 return sum;
249}
250
251
252/**
253 * Send an ICMP message to the dummy IP.
254 *
255 * @param my_ip source address (our ip address)
256 */
257static void
258send_icmp_echo (const struct in_addr *my_ip)
259{
260 char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)];
261 struct icmp_echo_header icmp_echo;
262 struct ip_header ip_pkt;
263 struct sockaddr_in dst;
264 size_t off;
265 int err;
266
267 off = 0;
268 ip_pkt.vers_ihl = 0x45;
269 ip_pkt.tos = 0;
270 ip_pkt.pkt_len = htons (sizeof(packet));
271 ip_pkt.id = htons (PACKET_ID);
272 ip_pkt.flags_frag_offset = 0;
273 ip_pkt.ttl = IPDEFTTL;
274 ip_pkt.proto = IPPROTO_ICMP;
275 ip_pkt.checksum = 0;
276 ip_pkt.src_ip = my_ip->s_addr;
277 ip_pkt.dst_ip = dummy.s_addr;
278 ip_pkt.checksum =
279 htons (calc_checksum ((uint16_t *) &ip_pkt,
280 sizeof(struct ip_header)));
281 GNUNET_memcpy (&packet[off],
282 &ip_pkt,
283 sizeof(struct ip_header));
284 off += sizeof(struct ip_header);
285
286 icmp_echo.type = ICMP_ECHO;
287 icmp_echo.code = 0;
288 icmp_echo.checksum = 0;
289 icmp_echo.reserved = 0;
290 icmp_echo.checksum =
291 htons (calc_checksum
292 ((uint16_t *) &icmp_echo,
293 sizeof(struct icmp_echo_header)));
294 GNUNET_memcpy (&packet[off],
295 &icmp_echo,
296 sizeof(struct icmp_echo_header));
297 off += sizeof(struct icmp_echo_header);
298
299 memset (&dst, 0, sizeof(dst));
300 dst.sin_family = AF_INET;
301#if HAVE_SOCKADDR_IN_SIN_LEN
302 dst.sin_len = sizeof(struct sockaddr_in);
303#endif
304 dst.sin_addr = dummy;
305 err = sendto (rawsock,
306 packet,
307 off,
308 0,
309 (struct sockaddr *) &dst,
310 sizeof(dst));
311 if (err < 0)
312 {
313#if VERBOSE
314 fprintf (stderr,
315 "sendto failed: %s\n",
316 strerror (errno));
317#endif
318 }
319 else if (sizeof(packet) != err)
320 {
321 fprintf (stderr,
322 "Error: partial send of ICMP message\n");
323 }
324}
325
326
327/**
328 * Send a UDP message to the dummy IP.
329 */
330static void
331send_udp ()
332{
333 struct sockaddr_in dst;
334 ssize_t err;
335
336 memset (&dst, 0, sizeof(dst));
337 dst.sin_family = AF_INET;
338#if HAVE_SOCKADDR_IN_SIN_LEN
339 dst.sin_len = sizeof(struct sockaddr_in);
340#endif
341 dst.sin_addr = dummy;
342 dst.sin_port = htons (NAT_TRAV_PORT);
343 err = sendto (udpsock,
344 NULL,
345 0,
346 0,
347 (struct sockaddr *) &dst,
348 sizeof(dst));
349 if (err < 0)
350 {
351#if VERBOSE
352 fprintf (stderr,
353 "sendto failed: %s\n",
354 strerror (errno));
355#endif
356 }
357 else if (0 != err)
358 {
359 fprintf (stderr,
360 "Error: partial send of ICMP message\n");
361 }
362}
363
364
365/**
366 * We've received an ICMP response. Process it.
367 */
368static void
369process_icmp_response ()
370{
371 char buf[65536];
372 ssize_t have;
373 struct in_addr source_ip;
374 struct ip_header ip_pkt;
375 struct icmp_ttl_exceeded_header icmp_ttl;
376 struct icmp_echo_header icmp_echo;
377 struct udp_header udp_pkt;
378 size_t off;
379 uint16_t port;
380
381 have = read (icmpsock, buf, sizeof(buf));
382 if (-1 == have)
383 {
384 fprintf (stderr,
385 "Error reading raw socket: %s\n",
386 strerror (errno));
387 return;
388 }
389#if VERBOSE
390 fprintf (stderr,
391 "Received message of %u bytes\n",
392 (unsigned int) have);
393#endif
394 if (have <
395 (ssize_t) (sizeof(struct ip_header)
396 + sizeof(struct icmp_ttl_exceeded_header)
397 + sizeof(struct ip_header)))
398 {
399 /* malformed */
400 return;
401 }
402 off = 0;
403 GNUNET_memcpy (&ip_pkt,
404 &buf[off],
405 sizeof(struct ip_header));
406 off += sizeof(struct ip_header);
407 GNUNET_memcpy (&icmp_ttl,
408 &buf[off],
409 sizeof(struct icmp_ttl_exceeded_header));
410 off += sizeof(struct icmp_ttl_exceeded_header);
411 if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
412 {
413 /* different type than what we want */
414 return;
415 }
416 /* grab source IP of 1st IP header */
417 source_ip.s_addr = ip_pkt.src_ip;
418
419 /* skip 2nd IP header */
420 GNUNET_memcpy (&ip_pkt,
421 &buf[off],
422 sizeof(struct ip_header));
423 off += sizeof(struct ip_header);
424
425 switch (ip_pkt.proto)
426 {
427 case IPPROTO_ICMP:
428 if (have !=
429 (sizeof(struct ip_header) * 2
430 + sizeof(struct icmp_ttl_exceeded_header)
431 + sizeof(struct icmp_echo_header)))
432 {
433 /* malformed */
434 return;
435 }
436 /* grab ICMP ECHO content */
437 GNUNET_memcpy (&icmp_echo,
438 &buf[off],
439 sizeof(struct icmp_echo_header));
440 port = (uint16_t) ntohl (icmp_echo.reserved);
441 break;
442
443 case IPPROTO_UDP:
444 if (have !=
445 (sizeof(struct ip_header) * 2
446 + sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header)))
447 {
448 /* malformed */
449 return;
450 }
451 /* grab UDP content */
452 GNUNET_memcpy (&udp_pkt,
453 &buf[off],
454 sizeof(struct udp_header));
455 port = ntohs (udp_pkt.length);
456 break;
457
458 default:
459 /* different type than what we want */
460 return;
461 }
462
463 if (port == 0)
464 fprintf (stdout, "%s\n",
465 inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)));
466 else
467 fprintf (stdout, "%s:%u\n",
468 inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)),
469 (unsigned int) port);
470 fflush (stdout);
471}
472
473
474/**
475 * Fully initialize the raw socket.
476 *
477 * @return -1 on error, 0 on success
478 */
479static int
480setup_raw_socket ()
481{
482 const int one = 1;
483
484 if (-1 ==
485 setsockopt (rawsock,
486 SOL_SOCKET,
487 SO_BROADCAST,
488 (char *) &one,
489 sizeof(one)))
490 {
491 fprintf (stderr,
492 "setsockopt failed: %s\n",
493 strerror (errno));
494 return -1;
495 }
496 if (-1 ==
497 setsockopt (rawsock,
498 IPPROTO_IP,
499 IP_HDRINCL,
500 (char *) &one,
501 sizeof(one)))
502 {
503 fprintf (stderr,
504 "setsockopt failed: %s\n",
505 strerror (errno));
506 return -1;
507 }
508 return 0;
509}
510
511
512/**
513 * Create a UDP socket for writing.
514 *
515 * @param my_ip source address (our ip address)
516 * @return -1 on error
517 */
518static int
519make_udp_socket (const struct in_addr *my_ip)
520{
521 int ret;
522 struct sockaddr_in addr;
523
524 ret = socket (AF_INET, SOCK_DGRAM, 0);
525 if (-1 == ret)
526 {
527 fprintf (stderr,
528 "Error opening UDP socket: %s\n",
529 strerror (errno));
530 return -1;
531 }
532 memset (&addr, 0, sizeof(addr));
533 addr.sin_family = AF_INET;
534#if HAVE_SOCKADDR_IN_SIN_LEN
535 addr.sin_len = sizeof(struct sockaddr_in);
536#endif
537 addr.sin_addr = *my_ip;
538 addr.sin_port = htons (NAT_TRAV_PORT);
539
540 if (0 != bind (ret,
541 (struct sockaddr *) &addr,
542 sizeof(addr)))
543 {
544 fprintf (stderr,
545 "Error binding UDP socket to port %u: %s\n",
546 NAT_TRAV_PORT,
547 strerror (errno));
548 (void) close (ret);
549 return -1;
550 }
551 return ret;
552}
553
554
555int
556main (int argc,
557 char *const *argv)
558{
559 struct in_addr external;
560 fd_set rs;
561 struct timeval tv;
562 uid_t uid;
563 unsigned int alt;
564 int icmp_eno;
565 int raw_eno;
566 int global_ret;
567
568 /* Create an ICMP raw socket for reading (we'll check errors later) */
569 icmpsock = socket (AF_INET,
570 SOCK_RAW,
571 IPPROTO_ICMP);
572 icmp_eno = errno;
573
574 /* Create an (ICMP) raw socket for writing (we'll check errors later) */
575 rawsock = socket (AF_INET,
576 SOCK_RAW,
577 IPPROTO_RAW);
578 raw_eno = errno;
579 udpsock = -1;
580
581 /* drop root rights */
582 uid = getuid ();
583#ifdef HAVE_SETRESUID
584 if (0 != setresuid (uid, uid, uid))
585 {
586 fprintf (stderr,
587 "Failed to setresuid: %s\n",
588 strerror (errno));
589 global_ret = 1;
590 goto error_exit;
591 }
592#else
593 if (0 != (setuid (uid) | seteuid (uid)))
594 {
595 fprintf (stderr,
596 "Failed to setuid: %s\n",
597 strerror (errno));
598 global_ret = 2;
599 goto error_exit;
600 }
601#endif
602
603 /* Now that we run without root rights, we can do error checking... */
604 if (2 != argc)
605 {
606 fprintf (stderr,
607 "This program must be started with our (internal NAT) IP as the only argument.\n");
608 global_ret = 3;
609 goto error_exit;
610 }
611 if (1 != inet_pton (AF_INET, argv[1], &external))
612 {
613 fprintf (stderr,
614 "Error parsing IPv4 address: %s\n",
615 strerror (errno));
616 global_ret = 4;
617 goto error_exit;
618 }
619 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
620 {
621 fprintf (stderr,
622 "Internal error converting dummy IP to binary.\n");
623 global_ret = 5;
624 goto error_exit;
625 }
626
627 /* error checking icmpsock */
628 if (-1 == icmpsock)
629 {
630 fprintf (stderr,
631 "Error opening RAW socket: %s\n",
632 strerror (icmp_eno));
633 global_ret = 6;
634 goto error_exit;
635 }
636 if (icmpsock >= FD_SETSIZE)
637 {
638 /* this could happen if we were started with a large number of already-open
639 file descriptors... */
640 fprintf (stderr,
641 "Socket number too large (%d > %u)\n",
642 icmpsock,
643 (unsigned int) FD_SETSIZE);
644 global_ret = 7;
645 goto error_exit;
646 }
647
648 /* error checking rawsock */
649 if (-1 == rawsock)
650 {
651 fprintf (stderr,
652 "Error opening RAW socket: %s\n",
653 strerror (raw_eno));
654 global_ret = 8;
655 goto error_exit;
656 }
657 /* no need to check 'rawsock' against FD_SETSIZE as it is never used
658 with 'select' */
659
660 if (0 != setup_raw_socket ())
661 {
662 global_ret = 9;
663 goto error_exit;
664 }
665
666 if (-1 == (udpsock = make_udp_socket (&external)))
667 {
668 global_ret = 10;
669 goto error_exit;
670 }
671
672 alt = 0;
673 while (1)
674 {
675 FD_ZERO (&rs);
676 FD_SET (icmpsock, &rs);
677 tv.tv_sec = 0;
678 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
679 if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
680 {
681 if (errno == EINTR)
682 continue;
683 fprintf (stderr,
684 "select failed: %s\n",
685 strerror (errno));
686 break;
687 }
688 if (1 == getppid ()) /* Check the parent process id, if 1 the parent has died, so we should die too */
689 break;
690 if (FD_ISSET (icmpsock, &rs))
691 {
692 process_icmp_response ();
693 continue;
694 }
695 if (0 == (++alt % 2))
696 send_icmp_echo (&external);
697 else
698 send_udp ();
699 }
700
701 /* select failed (internal error or OS out of resources) */
702 global_ret = 11;
703error_exit:
704 if (-1 != icmpsock)
705 (void) close (icmpsock);
706 if (-1 != rawsock)
707 (void) close (rawsock);
708 if (-1 != udpsock)
709 (void) close (udpsock);
710 return global_ret;
711}
712
713
714/* end of gnunet-helper-nat-server.c */
diff --git a/src/nat/gnunet-nat-client-script.sh b/src/nat/gnunet-nat-client-script.sh
deleted file mode 100755
index 4e4ccafad..000000000
--- a/src/nat/gnunet-nat-client-script.sh
+++ /dev/null
@@ -1,4 +0,0 @@
1#!/bin/sh
2IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"`
3echo "Using IP $IP, trying to connect to $1"
4./gnunet-nat-client-udp $IP $1
diff --git a/src/nat/gnunet-nat-server-script.sh b/src/nat/gnunet-nat-server-script.sh
deleted file mode 100755
index 42a8e639a..000000000
--- a/src/nat/gnunet-nat-server-script.sh
+++ /dev/null
@@ -1,4 +0,0 @@
1#!/bin/sh
2IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"`
3echo "Using IP $IP"
4./gnunet-nat-server $IP | sed -u -e "s/.*/.\/gnunet-nat-server-udp $IP &\&/" | sh
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c
deleted file mode 100644
index 0743a478d..000000000
--- a/src/nat/gnunet-nat.c
+++ /dev/null
@@ -1,476 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015, 2016 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 src/nat/gnunet-nat.c
23 * @brief Command-line tool to interact with the NAT service
24 * @author Christian Grothoff
25 * @author Bruno Cabral
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_nat_service.h"
30
31/**
32 * Value to return from #main().
33 */
34static int global_ret;
35
36/**
37 * Name of section in configuration file to use for
38 * additional options.
39 */
40static char *section_name;
41
42/**
43 * Flag set to 1 if we use IPPROTO_UDP.
44 */
45static int use_udp;
46
47/**
48 * Flag set to 1 if we are to listen for connection reversal requests.
49 */
50static int listen_reversal;
51
52/**
53 * Flag set to 1 if we use IPPROTO_TCP.
54 */
55static int use_tcp;
56
57/**
58 * Protocol to use.
59 */
60static uint8_t proto;
61
62/**
63 * Local address to use for connection reversal request.
64 */
65static char *local_addr;
66
67/**
68 * Remote address to use for connection reversal request.
69 */
70static char *remote_addr;
71
72/**
73 * Should we actually bind to #bind_addr and receive and process STUN requests?
74 */
75static int do_stun;
76
77/**
78 * Handle to NAT operation.
79 */
80static struct GNUNET_NAT_Handle *nh;
81
82/**
83 * Listen socket for STUN processing.
84 */
85static struct GNUNET_NETWORK_Handle *ls;
86
87/**
88 * Task for reading STUN packets.
89 */
90static struct GNUNET_SCHEDULER_Task *rtask;
91
92
93/**
94 * Test if all activities have finished, and if so,
95 * terminate.
96 */
97static void
98test_finished ()
99{
100 if (NULL != nh)
101 return;
102 if (NULL != rtask)
103 return;
104 GNUNET_SCHEDULER_shutdown ();
105}
106
107
108/**
109 * Signature of the callback passed to #GNUNET_NAT_register() for
110 * a function to call whenever our set of 'valid' addresses changes.
111 *
112 * @param cls closure, NULL
113 * @param app_ctx[in,out] location where the app can store stuff
114 * on add and retrieve it on remove
115 * @param add_remove #GNUNET_YES to add a new public IP address,
116 * #GNUNET_NO to remove a previous (now invalid) one
117 * @param ac address class the address belongs to
118 * @param addr either the previous or the new public IP address
119 * @param addrlen actual length of the @a addr
120 */
121static void
122address_cb (void *cls,
123 void **app_ctx,
124 int add_remove,
125 enum GNUNET_NAT_AddressClass ac,
126 const struct sockaddr *addr,
127 socklen_t addrlen)
128{
129 (void) cls;
130 (void) app_ctx;
131
132 fprintf (stdout,
133 "%s %s (%d)\n",
134 add_remove ? "+" : "-",
135 GNUNET_a2s (addr, addrlen),
136 (int) ac);
137}
138
139
140/**
141 * Signature of the callback passed to #GNUNET_NAT_register().
142 * for a function to call whenever someone asks us to do connection
143 * reversal.
144 *
145 * @param cls closure, NULL
146 * @param remote_addr public IP address of the other peer
147 * @param remote_addrlen actual length of the @a remote_addr
148 */
149static void
150reversal_cb (void *cls,
151 const struct sockaddr *remote_addr,
152 socklen_t remote_addrlen)
153{
154 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
155 "Connection reversal requested by %s\n",
156 GNUNET_a2s (remote_addr, remote_addrlen));
157}
158
159
160/**
161 * Task run on shutdown.
162 *
163 * @param cls NULL
164 */
165static void
166do_shutdown (void *cls)
167{
168 if (NULL != nh)
169 {
170 GNUNET_NAT_unregister (nh);
171 nh = NULL;
172 }
173 if (NULL != ls)
174 {
175 GNUNET_NETWORK_socket_close (ls);
176 ls = NULL;
177 }
178 if (NULL != rtask)
179 {
180 GNUNET_SCHEDULER_cancel (rtask);
181 rtask = NULL;
182 }
183}
184
185
186/**
187 * Task to receive incoming packets for STUN processing.
188 */
189static void
190stun_read_task (void *cls)
191{
192 ssize_t size;
193
194 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
195 ls,
196 &stun_read_task,
197 NULL);
198 size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
199 if (size > 0)
200 {
201 GNUNET_break (0);
202 GNUNET_SCHEDULER_shutdown ();
203 global_ret = 1;
204 return;
205 }
206 {
207 char buf[size + 1];
208 struct sockaddr_storage sa;
209 socklen_t salen = sizeof(sa);
210 ssize_t ret;
211
212 ret = GNUNET_NETWORK_socket_recvfrom (ls,
213 buf,
214 size + 1,
215 (struct sockaddr *) &sa,
216 &salen);
217 if (ret != size)
218 {
219 GNUNET_break (0);
220 GNUNET_SCHEDULER_shutdown ();
221 global_ret = 1;
222 return;
223 }
224 (void) GNUNET_NAT_stun_handle_packet (nh,
225 (const struct sockaddr *) &sa,
226 salen,
227 buf,
228 ret);
229 }
230}
231
232
233/**
234 * Main function that will be run.
235 *
236 * @param cls closure
237 * @param args remaining command-line arguments
238 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
239 * @param c configuration
240 */
241static void
242run (void *cls,
243 char *const *args,
244 const char *cfgfile,
245 const struct GNUNET_CONFIGURATION_Handle *c)
246{
247 uint8_t af;
248 struct sockaddr *local_sa;
249 struct sockaddr *remote_sa;
250 socklen_t local_len;
251 size_t remote_len;
252
253 if (use_tcp && use_udp)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n");
256 global_ret = 1;
257 return;
258 }
259 proto = 0;
260 if (use_tcp)
261 proto = IPPROTO_TCP;
262 if (use_udp)
263 proto = IPPROTO_UDP;
264
265 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
266
267 if (0 == proto)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n");
270 global_ret = 1;
271 return;
272 }
273 local_len = 0;
274 local_sa = NULL;
275 remote_len = 0;
276 remote_sa = NULL;
277 if (NULL != local_addr)
278 {
279 local_len =
280 (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa);
281 if (0 == local_len)
282 {
283 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
284 "Invalid socket address `%s'\n",
285 local_addr);
286 goto fail_and_shutdown;
287 }
288 }
289
290 if (NULL != remote_addr)
291 {
292 remote_len =
293 GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa);
294 if (0 == remote_len)
295 {
296 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
297 "Invalid socket address `%s'\n",
298 remote_addr);
299 goto fail_and_shutdown;
300 }
301 }
302
303 if (NULL != local_addr)
304 {
305 if (NULL == section_name)
306 section_name = GNUNET_strdup ("undefined");
307 nh = GNUNET_NAT_register (c,
308 section_name,
309 proto,
310 1,
311 (const struct sockaddr **) &local_sa,
312 &local_len,
313 &address_cb,
314 (listen_reversal) ? &reversal_cb : NULL,
315 NULL);
316 }
317 else if (listen_reversal)
318 {
319 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
320 "Use of `-W` only effective in combination with `-i`\n");
321 goto fail_and_shutdown;
322 }
323
324 if (NULL != remote_addr)
325 {
326 int ret;
327
328 if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len))
329 {
330 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
331 "Require IPv4 local address to initiate connection reversal\n");
332 goto fail_and_shutdown;
333 }
334 if (sizeof(struct sockaddr_in) != remote_len)
335 {
336 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
337 "Require IPv4 reversal target address\n");
338 goto fail_and_shutdown;
339 }
340 GNUNET_assert (AF_INET == local_sa->sa_family);
341 GNUNET_assert (AF_INET == remote_sa->sa_family);
342 ret = GNUNET_NAT_request_reversal (nh,
343 (const struct sockaddr_in *) local_sa,
344 (const struct sockaddr_in *) remote_sa);
345 switch (ret)
346 {
347 case GNUNET_SYSERR:
348 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
349 "Connection reversal internal error\n");
350 break;
351
352 case GNUNET_NO:
353 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
354 "Connection reversal unavailable\n");
355 break;
356
357 case GNUNET_OK:
358 /* operation in progress */
359 break;
360 }
361 }
362
363 if (do_stun)
364 {
365 if (NULL == local_addr)
366 {
367 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
368 "Require local address to support STUN requests\n");
369 goto fail_and_shutdown;
370 }
371 if (IPPROTO_UDP != proto)
372 {
373 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n");
374 goto fail_and_shutdown;
375 }
376 ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP);
377 if (NULL == ls)
378 {
379 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n");
380 goto fail_and_shutdown;
381 }
382 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len))
383 {
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 "Failed to bind to %s: %s\n",
386 GNUNET_a2s (local_sa, local_len),
387 strerror (errno));
388 goto fail_and_shutdown;
389 }
390 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
391 ls,
392 &stun_read_task,
393 NULL);
394 }
395 GNUNET_free (remote_sa);
396 GNUNET_free (local_sa);
397 test_finished ();
398 return;
399fail_and_shutdown:
400 global_ret = 1;
401 GNUNET_SCHEDULER_shutdown ();
402 GNUNET_free (remote_sa);
403 GNUNET_free (local_sa);
404}
405
406
407/**
408 * Main function of gnunet-nat
409 *
410 * @param argc number of command-line arguments
411 * @param argv command line
412 * @return 0 on success, -1 on error
413 */
414int
415main (int argc, char *const argv[])
416{
417 struct GNUNET_GETOPT_CommandLineOption options[] = {
418 GNUNET_GETOPT_option_string (
419 'i',
420 "in",
421 "ADDRESS",
422 gettext_noop ("which IP and port are we locally using to bind/listen to"),
423 &local_addr),
424
425 GNUNET_GETOPT_option_string (
426 'r',
427 "remote",
428 "ADDRESS",
429 gettext_noop (
430 "which remote IP and port should be asked for connection reversal"),
431 &remote_addr),
432
433 GNUNET_GETOPT_option_string (
434 'S',
435 "section",
436 NULL,
437 gettext_noop (
438 "name of configuration section to find additional options, such as manual host punching data"),
439 &section_name),
440
441 GNUNET_GETOPT_option_flag ('s',
442 "stun",
443 gettext_noop ("enable STUN processing"),
444 &do_stun),
445
446 GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp),
447
448 GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp),
449
450 GNUNET_GETOPT_option_flag ('W',
451 "watch",
452 gettext_noop (
453 "watch for connection reversal requests"),
454 &listen_reversal),
455 GNUNET_GETOPT_OPTION_END
456 };
457
458 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
459 return 2;
460 if (GNUNET_OK !=
461 GNUNET_PROGRAM_run (argc,
462 argv,
463 "gnunet-nat [options]",
464 _ ("GNUnet NAT traversal autoconfigure daemon"),
465 options,
466 &run,
467 NULL))
468 {
469 global_ret = 1;
470 }
471 GNUNET_free_nz ((void *) argv);
472 return global_ret;
473}
474
475
476/* end of gnunet-nat.c */
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 */
76struct 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 */
95struct 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 */
141struct 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 */
240struct 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 */
280static 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 */
285static struct GNUNET_TIME_Relative dyndns_frequency;
286
287/**
288 * Handle to our current configuration.
289 */
290static const struct GNUNET_CONFIGURATION_Handle *cfg;
291
292/**
293 * Handle to the statistics service.
294 */
295static struct GNUNET_STATISTICS_Handle *stats;
296
297/**
298 * Task scheduled to periodically scan our network interfaces.
299 */
300static struct GNUNET_SCHEDULER_Task *scan_task;
301
302/**
303 * Head of client DLL.
304 */
305static struct ClientHandle *ch_head;
306
307/**
308 * Tail of client DLL.
309 */
310static struct ClientHandle *ch_tail;
311
312/**
313 * Head of DLL of local addresses.
314 */
315static struct LocalAddressList *lal_head;
316
317/**
318 * Tail of DLL of local addresses.
319 */
320static struct LocalAddressList *lal_tail;
321
322/**
323 * Kept in a DLL.
324 */
325static struct StunExternalIP *se_head;
326
327/**
328 * Kept in a DLL.
329 */
330static 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 */
336int 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 */
342int enable_ipscan;
343
344/**
345 * Remove and free an entry from the #lal_head DLL.
346 *
347 * @param lal entry to free
348 */
349static void
350free_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 */
372static void
373destroy_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 */
390static int
391check_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 */
452static int
453match_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 */
478static int
479match_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 */
522static int
523is_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 */
541static int
542is_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 */
554struct 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 */
581static int
582ifc_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 */
659static void
660notify_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 */
695static void
696check_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 */
823static void
824notify_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 */
844static void
845notify_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 */
923static void
924reversal_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 */
980static void
981run_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 */
1093static void
1094upnp_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 */
1190static void
1191dyndns_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 */
1203static void
1204process_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 */
1306static void
1307dyndns_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 */
1339static void
1340lookup_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 */
1451static void
1452handle_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 (&notify_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 */
1591static int
1592check_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 */
1619static void
1620notify_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 */
1657static void
1658stun_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 */
1679static void
1680handle_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 */
1792static int
1793check_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 */
1818static void
1819handle_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 */
1871static void
1872shutdown_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 */
1907static void
1908run (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 */
1969static void *
1970client_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 */
1993static void
1994client_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 */
2044GNUNET_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 */
2072void __attribute__ ((constructor))
2073GNUNET_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 */
diff --git a/src/nat/gnunet-service-nat.h b/src/nat/gnunet-service-nat.h
deleted file mode 100644
index 5717306bb..000000000
--- a/src/nat/gnunet-service-nat.h
+++ /dev/null
@@ -1,35 +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.h
23 * @brief network address translation traversal service
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_NAT_H
27#define GNUNET_SERVICE_NAT_H
28
29/**
30 * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
31 * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
32 */
33extern int enable_upnp;
34
35#endif
diff --git a/src/nat/gnunet-service-nat_externalip.c b/src/nat/gnunet-service-nat_externalip.c
deleted file mode 100644
index c2625be2d..000000000
--- a/src/nat/gnunet-service-nat_externalip.c
+++ /dev/null
@@ -1,316 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 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 * Code to figure out what our external IPv4 address(es) might
22 * be (external IPv4s are what is seen on the rest of the Internet).
23 *
24 * This can be implemented using different methods, and we allow
25 * the main service to be notified about changes to what we believe
26 * is our external IPv4 address.
27 *
28 * Note that this is explicitly only about NATed systems; if one
29 * of our network interfaces has a global IP address this does
30 * not count as "external".
31 *
32 * @file nat/gnunet-service-nat_externalip.c
33 * @brief Functions for monitoring external IPv4 addresses
34 * @author Christian Grothoff
35 */
36#include "platform.h"
37#include <math.h>
38#include "gnunet_util_lib.h"
39#include "gnunet_protocols.h"
40#include "gnunet_signatures.h"
41#include "gnunet_statistics_service.h"
42#include "gnunet_resolver_service.h"
43#include "gnunet_nat_service.h"
44#include "gnunet-service-nat.h"
45#include "gnunet-service-nat_externalip.h"
46#include "gnunet-service-nat_stun.h"
47#include "gnunet-service-nat_mini.h"
48#include "gnunet-service-nat_helper.h"
49#include "nat.h"
50#include <gcrypt.h>
51
52
53/**
54 * How long do we wait until we re-try running `external-ip` if the
55 * command failed to terminate nicely?
56 */
57#define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply ( \
58 GNUNET_TIME_UNIT_MINUTES, 15)
59
60/**
61 * How long do we wait until we re-try running `external-ip` if the
62 * command failed (but terminated)?
63 */
64#define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply ( \
65 GNUNET_TIME_UNIT_MINUTES, 30)
66
67/**
68 * How long do we wait until we re-try running `external-ip` if the
69 * command succeeded?
70 */
71#define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply ( \
72 GNUNET_TIME_UNIT_MINUTES, 5)
73
74
75/**
76 * Handle to monitor for external IP changes.
77 */
78struct GN_ExternalIPMonitor
79{
80 /**
81 * Kept in DLL.
82 */
83 struct GN_ExternalIPMonitor *next;
84
85 /**
86 * Kept in DLL.
87 */
88 struct GN_ExternalIPMonitor *prev;
89
90 /**
91 * Function to call when we believe our external IPv4 address changed.
92 */
93 GN_NotifyExternalIPv4Change cb;
94
95 /**
96 * Closure for @e cb.
97 */
98 void *cb_cls;
99};
100
101
102/**
103 * List of monitors, kept in DLL.
104 */
105static struct GN_ExternalIPMonitor *mon_head;
106
107/**
108 * List of monitors, kept in DLL.
109 */
110static struct GN_ExternalIPMonitor *mon_tail;
111
112/**
113 * Task run to obtain our external IP (if #enable_upnp is set
114 * and if we find we have a NATed IP address).
115 */
116static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
117
118/**
119 * Handle to our operation to run `external-ip`.
120 */
121static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
122
123/**
124 * What is our external IP address as claimed by `external-ip`?
125 * 0 for unknown.
126 */
127static struct in_addr mini_external_ipv4;
128
129
130/**
131 * Tell relevant clients about a change in our external
132 * IPv4 address.
133 *
134 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
135 * @param v4 the external address that changed
136 */
137static void
138notify_monitors_external_ipv4_change (int add,
139 const struct in_addr *v4)
140{
141 for (struct GN_ExternalIPMonitor *mon = mon_head;
142 NULL != mon;
143 mon = mon->next)
144 mon->cb (mon->cb_cls,
145 v4,
146 add);
147}
148
149
150/**
151 * Task used to run `external-ip` to get our external IPv4
152 * address and pass it to NATed clients if possible.
153 *
154 * @param cls NULL
155 */
156static void
157run_external_ip (void *cls);
158
159
160/**
161 * We learn our current external IP address. If it changed,
162 * notify all of our applicable clients. Also re-schedule
163 * #run_external_ip with an appropriate timeout.
164 *
165 * @param cls NULL
166 * @param addr the address, NULL on errors
167 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
168 */
169static void
170handle_external_ip (void *cls,
171 const struct in_addr *addr,
172 enum GNUNET_NAT_StatusCode result)
173{
174 char buf[INET_ADDRSTRLEN];
175
176 probe_external_ip_op = NULL;
177 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
178 probe_external_ip_task
179 = GNUNET_SCHEDULER_add_delayed ((NULL == addr)
180 ? EXTERN_IP_RETRY_FAILURE
181 : EXTERN_IP_RETRY_SUCCESS,
182 &run_external_ip,
183 NULL);
184 switch (result)
185 {
186 case GNUNET_NAT_ERROR_SUCCESS:
187 GNUNET_assert (NULL != addr);
188 if (addr->s_addr == mini_external_ipv4.s_addr)
189 return; /* not change */
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Our external IP is now %s\n",
192 inet_ntop (AF_INET,
193 addr,
194 buf,
195 sizeof(buf)));
196 if (0 != mini_external_ipv4.s_addr)
197 notify_monitors_external_ipv4_change (GNUNET_NO,
198 &mini_external_ipv4);
199 mini_external_ipv4 = *addr;
200 notify_monitors_external_ipv4_change (GNUNET_YES,
201 &mini_external_ipv4);
202 break;
203
204 default:
205 if (0 != mini_external_ipv4.s_addr)
206 notify_monitors_external_ipv4_change (GNUNET_NO,
207 &mini_external_ipv4);
208 mini_external_ipv4.s_addr = 0;
209 break;
210 }
211}
212
213
214/**
215 * Task used to run `external-ip` to get our external IPv4
216 * address and pass it to NATed clients if possible.
217 *
218 * @param cls NULL
219 */
220static void
221run_external_ip (void *cls)
222{
223 probe_external_ip_task
224 = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
225 &run_external_ip,
226 NULL);
227 if (NULL != probe_external_ip_op)
228 {
229 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
230 probe_external_ip_op = NULL;
231 }
232 probe_external_ip_op
233 = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
234 NULL);
235}
236
237
238/**
239 * We have changed our opinion about being NATed in the first
240 * place. Adapt our probing.
241 *
242 * @param have_nat #GNUNET_YES if we believe we are behind NAT
243 */
244void
245GN_nat_status_changed (int have_nat)
246{
247 if (GNUNET_YES != enable_upnp)
248 return;
249 if ((GNUNET_YES == have_nat) &&
250 (NULL == probe_external_ip_task) &&
251 (NULL == probe_external_ip_op))
252 {
253 probe_external_ip_task
254 = GNUNET_SCHEDULER_add_now (&run_external_ip,
255 NULL);
256 return;
257 }
258 if (GNUNET_NO == have_nat)
259 {
260 if (NULL != probe_external_ip_task)
261 {
262 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
263 probe_external_ip_task = NULL;
264 }
265 if (NULL != probe_external_ip_op)
266 {
267 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
268 probe_external_ip_op = NULL;
269 }
270 }
271}
272
273
274/**
275 * Start monitoring external IPv4 addresses.
276 *
277 * @param cb function to call on changes
278 * @param cb_cls closure for @a cb
279 * @return handle to cancel
280 */
281struct GN_ExternalIPMonitor *
282GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
283 void *cb_cls)
284{
285 struct GN_ExternalIPMonitor *mon;
286
287 mon = GNUNET_new (struct GN_ExternalIPMonitor);
288 mon->cb = cb;
289 mon->cb_cls = cb_cls;
290 GNUNET_CONTAINER_DLL_insert (mon_head,
291 mon_tail,
292 mon);
293 if (0 != mini_external_ipv4.s_addr)
294 cb (cb_cls,
295 &mini_external_ipv4,
296 GNUNET_YES);
297 return mon;
298}
299
300
301/**
302 * Stop calling monitor.
303 *
304 * @param mon monitor to call
305 */
306void
307GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon)
308{
309 GNUNET_CONTAINER_DLL_remove (mon_head,
310 mon_tail,
311 mon);
312 GNUNET_free (mon);
313}
314
315
316/* end of gnunet-service-nat_externalip.c */
diff --git a/src/nat/gnunet-service-nat_externalip.h b/src/nat/gnunet-service-nat_externalip.h
deleted file mode 100644
index 6b3467fab..000000000
--- a/src/nat/gnunet-service-nat_externalip.h
+++ /dev/null
@@ -1,92 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 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 * Code to figure out what our external IPv4 address(es) might
22 * be (external IPv4s are what is seen on the rest of the Internet).
23 *
24 * This can be implemented using different methods, and we allow
25 * the main service to be notified about changes to what we believe
26 * is our external IPv4 address.
27 *
28 * Note that this is explicitly only about NATed systems; if one
29 * of our network interfaces has a global IP address this does
30 * not count as "external".
31 *
32 * @file nat/gnunet-service-nat_externalip.h
33 * @brief Functions for monitoring external IPv4 addresses
34 * @author Christian Grothoff
35 */
36#ifndef GNUNET_SERVICE_NAT_EXTERNALIP_H
37#define GNUNET_SERVICE_NAT_EXTERNALIP_H
38
39#include "platform.h"
40
41
42/**
43 * We have changed our opinion about being NATed in the first
44 * place. Adapt our probing.
45 *
46 * @param have_nat #GNUNET_YES if we believe we are behind NAT
47 */
48void
49GN_nat_status_changed (int have_nat);
50
51
52/**
53 * Function we call when we believe our external IPv4 address changed.
54 *
55 * @param cls closure
56 * @param ip address to add/remove
57 * @param add_remove #GNUNET_YES to add, #GNUNET_NO to remove
58 */
59typedef void
60(*GN_NotifyExternalIPv4Change)(void *cls,
61 const struct in_addr *ip,
62 int add_remove);
63
64
65/**
66 * Handle to monitor for external IP changes.
67 */
68struct GN_ExternalIPMonitor;
69
70
71/**
72 * Start monitoring external IPv4 addresses.
73 *
74 * @param cb function to call on changes
75 * @param cb_cls closure for @a cb
76 * @return handle to cancel
77 */
78struct GN_ExternalIPMonitor *
79GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
80 void *cb_cls);
81
82
83/**
84 * Stop calling monitor.
85 *
86 * @param mon monitor to call
87 */
88void
89GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon);
90
91
92#endif
diff --git a/src/nat/gnunet-service-nat_helper.c b/src/nat/gnunet-service-nat_helper.c
deleted file mode 100644
index bd1645d25..000000000
--- a/src/nat/gnunet-service-nat_helper.c
+++ /dev/null
@@ -1,401 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 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_helper.c
23 * @brief runs the gnunet-helper-nat-server
24 * @author Milan Bouchet-Valat
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet-service-nat_helper.h"
30
31
32/**
33 * Information we keep per NAT helper process.
34 */
35struct HelperContext
36{
37 /**
38 * IP address we pass to the NAT helper.
39 */
40 struct in_addr internal_address;
41
42 /**
43 * Function to call if we receive a reversal request.
44 */
45 GN_ReversalCallback cb;
46
47 /**
48 * Closure for @e cb.
49 */
50 void *cb_cls;
51
52 /**
53 * How long do we wait for restarting a crashed gnunet-helper-nat-server?
54 */
55 struct GNUNET_TIME_Relative server_retry_delay;
56
57 /**
58 * ID of select gnunet-helper-nat-server stdout read task
59 */
60 struct GNUNET_SCHEDULER_Task *server_read_task;
61
62 /**
63 * The process id of the server process (if behind NAT)
64 */
65 struct GNUNET_OS_Process *server_proc;
66
67 /**
68 * stdout pipe handle for the gnunet-helper-nat-server process
69 */
70 struct GNUNET_DISK_PipeHandle *server_stdout;
71
72 /**
73 * stdout file handle (for reading) for the gnunet-helper-nat-server process
74 */
75 const struct GNUNET_DISK_FileHandle *server_stdout_handle;
76
77 /**
78 * Handle to the GNUnet configuration
79 */
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
81};
82
83
84/**
85 * Task that restarts the gnunet-helper-nat-server process after a crash
86 * after a certain delay.
87 *
88 * @param cls a `struct HelperContext`
89 */
90static void
91restart_nat_server (void *cls);
92
93
94/**
95 * Try again starting the helper later
96 *
97 * @param h context of the helper
98 */
99static void
100try_again (struct HelperContext *h)
101{
102 GNUNET_assert (NULL == h->server_read_task);
103 h->server_retry_delay = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay);
104 h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
105 &restart_nat_server,
106 h);
107}
108
109
110/**
111 * We have been notified that gnunet-helper-nat-server has written
112 * something to stdout. Handle the output, then reschedule this
113 * function to be called again once more is available.
114 *
115 * @param cls the `struct HelperContext`
116 */
117static void
118nat_server_read (void *cls)
119{
120 struct HelperContext *h = cls;
121 char mybuf[40];
122 ssize_t bytes;
123 int port;
124 const char *port_start;
125 struct sockaddr_in sin_addr;
126
127 h->server_read_task = NULL;
128 memset (mybuf, 0, sizeof(mybuf));
129 bytes =
130 GNUNET_DISK_file_read (h->server_stdout_handle, mybuf, sizeof(mybuf));
131 if (bytes < 1)
132 {
133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
134 "Finished reading from server stdout with code: %d\n",
135 (int) bytes);
136 if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG))
137 GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, "nat", "kill");
138 GNUNET_OS_process_wait (h->server_proc);
139 GNUNET_OS_process_destroy (h->server_proc);
140 h->server_proc = NULL;
141 GNUNET_DISK_pipe_close (h->server_stdout);
142 h->server_stdout = NULL;
143 h->server_stdout_handle = NULL;
144 try_again (h);
145 return;
146 }
147
148 port_start = NULL;
149 for (size_t i = 0; i < sizeof(mybuf); i++)
150 {
151 if (mybuf[i] == '\n')
152 {
153 mybuf[i] = '\0';
154 break;
155 }
156 if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf)))
157 {
158 mybuf[i] = '\0';
159 port_start = &mybuf[i + 1];
160 }
161 }
162
163 /* construct socket address of sender */
164 memset (&sin_addr, 0, sizeof(sin_addr));
165 sin_addr.sin_family = AF_INET;
166#if HAVE_SOCKADDR_IN_SIN_LEN
167 sin_addr.sin_len = sizeof(sin_addr);
168#endif
169 if ((NULL == port_start) || (1 != sscanf (port_start, "%d", &port)) ||
170 (-1 == inet_pton (AF_INET, mybuf, &sin_addr.sin_addr)))
171 {
172 /* should we restart gnunet-helper-nat-server? */
173 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
174 _ (
175 "gnunet-helper-nat-server generated malformed address `%s'\n"),
176 mybuf);
177 h->server_read_task =
178 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
179 h->server_stdout_handle,
180 &nat_server_read,
181 h);
182 return;
183 }
184 sin_addr.sin_port = htons ((uint16_t) port);
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 "gnunet-helper-nat-server read: %s:%d\n",
187 mybuf,
188 port);
189 h->cb (h->cb_cls, &sin_addr);
190 h->server_read_task =
191 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
192 h->server_stdout_handle,
193 &nat_server_read,
194 h);
195}
196
197
198/**
199 * Task that restarts the gnunet-helper-nat-server process after a crash
200 * after a certain delay.
201 *
202 * @param cls a `struct HelperContext`
203 */
204static void
205restart_nat_server (void *cls)
206{
207 struct HelperContext *h = cls;
208 char *binary;
209 char ia[INET_ADDRSTRLEN];
210
211 h->server_read_task = NULL;
212 GNUNET_assert (NULL !=
213 inet_ntop (AF_INET, &h->internal_address, ia, sizeof(ia)));
214 /* Start the server process */
215 binary = GNUNET_OS_get_suid_binary_path (h->cfg, "gnunet-helper-nat-server");
216 if (GNUNET_YES != GNUNET_OS_check_helper_binary (binary, GNUNET_YES, ia))
217 {
218 /* move instantly to max delay, as this is unlikely to be fixed */
219 h->server_retry_delay = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD;
220 GNUNET_free (binary);
221 try_again (h);
222 return;
223 }
224 h->server_stdout =
225 GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
226 if (NULL == h->server_stdout)
227 {
228 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
229 GNUNET_free (binary);
230 try_again (h);
231 return;
232 }
233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
234 "Starting `%s' at `%s'\n",
235 "gnunet-helper-nat-server",
236 ia);
237 h->server_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE,
238 NULL,
239 h->server_stdout,
240 NULL,
241 binary,
242 "gnunet-helper-nat-server",
243 ia,
244 NULL);
245 GNUNET_free (binary);
246 if (NULL == h->server_proc)
247 {
248 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
249 _ ("Failed to start %s\n"),
250 "gnunet-helper-nat-server");
251 GNUNET_DISK_pipe_close (h->server_stdout);
252 h->server_stdout = NULL;
253 try_again (h);
254 return;
255 }
256 /* Close the write end of the read pipe */
257 GNUNET_DISK_pipe_close_end (h->server_stdout, GNUNET_DISK_PIPE_END_WRITE);
258 h->server_stdout_handle =
259 GNUNET_DISK_pipe_handle (h->server_stdout, GNUNET_DISK_PIPE_END_READ);
260 h->server_read_task =
261 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
262 h->server_stdout_handle,
263 &nat_server_read,
264 h);
265}
266
267
268/**
269 * Start the gnunet-helper-nat-server and process incoming
270 * requests.
271 *
272 * @param internal_address
273 * @param cb function to call if we receive a request
274 * @param cb_cls closure for @a cb
275 * @param cfg Handle to the GNUnet configuration
276 * @return NULL on error
277 */
278struct HelperContext *
279GN_start_gnunet_nat_server_ (const struct in_addr *internal_address,
280 GN_ReversalCallback cb,
281 void *cb_cls,
282 const struct GNUNET_CONFIGURATION_Handle *cfg)
283{
284 struct HelperContext *h;
285
286 h = GNUNET_new (struct HelperContext);
287 h->cb = cb;
288 h->cb_cls = cb_cls;
289 h->internal_address = *internal_address;
290 h->cfg = cfg;
291 restart_nat_server (h);
292 if (NULL == h->server_stdout)
293 {
294 GN_stop_gnunet_nat_server_ (h);
295 return NULL;
296 }
297 return h;
298}
299
300
301/**
302 * Start the gnunet-helper-nat-server and process incoming
303 * requests.
304 *
305 * @param h helper context to stop
306 */
307void
308GN_stop_gnunet_nat_server_ (struct HelperContext *h)
309{
310 if (NULL != h->server_read_task)
311 {
312 GNUNET_SCHEDULER_cancel (h->server_read_task);
313 h->server_read_task = NULL;
314 }
315 if (NULL != h->server_proc)
316 {
317 if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG))
318 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
319 GNUNET_OS_process_wait (h->server_proc);
320 GNUNET_OS_process_destroy (h->server_proc);
321 h->server_proc = NULL;
322 GNUNET_DISK_pipe_close (h->server_stdout);
323 h->server_stdout = NULL;
324 h->server_stdout_handle = NULL;
325 }
326 if (NULL != h->server_stdout)
327 {
328 GNUNET_DISK_pipe_close (h->server_stdout);
329 h->server_stdout = NULL;
330 h->server_stdout_handle = NULL;
331 }
332 GNUNET_free (h);
333}
334
335
336/**
337 * We want to connect to a peer that is behind NAT. Run the
338 * gnunet-helper-nat-client to send dummy ICMP responses to cause
339 * that peer to connect to us (connection reversal).
340 *
341 * @param internal_address out internal address to use
342 * @param internal_port port to use
343 * @param remote_v4 the address of the peer (IPv4-only)
344 * @param cfg handle to the GNUnet configuration
345 * @return #GNUNET_SYSERR on error,
346 * #GNUNET_OK otherwise
347 */
348int
349GN_request_connection_reversal (const struct in_addr *internal_address,
350 uint16_t internal_port,
351 const struct in_addr *remote_v4,
352 const struct GNUNET_CONFIGURATION_Handle *cfg)
353{
354 char intv4[INET_ADDRSTRLEN];
355 char remv4[INET_ADDRSTRLEN];
356 char port_as_string[6];
357 struct GNUNET_OS_Process *proc;
358 char *binary;
359
360 if (NULL == inet_ntop (AF_INET, internal_address, intv4, INET_ADDRSTRLEN))
361 {
362 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
363 return GNUNET_SYSERR;
364 }
365 if (NULL == inet_ntop (AF_INET, remote_v4, remv4, INET_ADDRSTRLEN))
366 {
367 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
368 return GNUNET_SYSERR;
369 }
370 GNUNET_snprintf (port_as_string,
371 sizeof(port_as_string),
372 "%d",
373 internal_port);
374 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
375 "Running gnunet-helper-nat-client %s %s %u\n",
376 intv4,
377 remv4,
378 internal_port);
379 binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-nat-client");
380 proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE,
381 NULL,
382 NULL,
383 NULL,
384 binary,
385 "gnunet-helper-nat-client",
386 intv4,
387 remv4,
388 port_as_string,
389 NULL);
390 GNUNET_free (binary);
391 if (NULL == proc)
392 return GNUNET_SYSERR;
393 /* we know that the gnunet-helper-nat-client will terminate virtually
394 * instantly */
395 GNUNET_OS_process_wait (proc);
396 GNUNET_OS_process_destroy (proc);
397 return GNUNET_OK;
398}
399
400
401/* end of gnunet-service-nat_helper.c */
diff --git a/src/nat/gnunet-service-nat_helper.h b/src/nat/gnunet-service-nat_helper.h
deleted file mode 100644
index dc6b9ae61..000000000
--- a/src/nat/gnunet-service-nat_helper.h
+++ /dev/null
@@ -1,95 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 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_helper.h
23 * @brief runs the gnunet-helper-nat-server
24 * @author Milan Bouchet-Valat
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31/**
32 * Information we keep per NAT helper process.
33 */
34struct HelperContext;
35
36
37/**
38 * Function called whenever we get a connection reversal
39 * request from another peer.
40 *
41 * @param cls closure
42 * @param ra IP address of the peer who wants us to connect to it
43 */
44typedef void
45(*GN_ReversalCallback) (void *cls,
46 const struct sockaddr_in *ra);
47
48
49/**
50 * Start the gnunet-helper-nat-server and process incoming
51 * requests.
52 *
53 * @param internal_address
54 * @param cb function to call if we receive a request
55 * @param cb_cls closure for @a cb
56 * @param cfg handle to the GNUnet configuration
57 * @return NULL on error
58 */
59struct HelperContext *
60GN_start_gnunet_nat_server_ (const struct in_addr *internal_address,
61 GN_ReversalCallback cb,
62 void *cb_cls,
63 const struct GNUNET_CONFIGURATION_Handle *cfg);
64
65
66/**
67 * Start the gnunet-helper-nat-server and process incoming
68 * requests.
69 *
70 * @param h helper context to stop
71 */
72void
73GN_stop_gnunet_nat_server_ (struct HelperContext *h);
74
75
76/**
77 * We want to connect to a peer that is behind NAT. Run the
78 * gnunet-helper-nat-client to send dummy ICMP responses to cause
79 * that peer to connect to us (connection reversal).
80 *
81 * @param internal_address out internal address to use
82 * @param internal_port internal port to use
83 * @param remote_v4 the address of the peer (IPv4-only)
84 * @param cfg handle to the GNUnet configuration
85 * @return #GNUNET_SYSERR on error,
86 * #GNUNET_OK otherwise
87 */
88int
89GN_request_connection_reversal (const struct in_addr *internal_address,
90 uint16_t internal_port,
91 const struct in_addr *remote_v4,
92 const struct GNUNET_CONFIGURATION_Handle *cfg);
93
94
95/* end of gnunet-service-nat_helper.h */
diff --git a/src/nat/gnunet-service-nat_mini.c b/src/nat/gnunet-service-nat_mini.c
deleted file mode 100644
index 24f77d9cc..000000000
--- a/src/nat/gnunet-service-nat_mini.c
+++ /dev/null
@@ -1,707 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify 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_mini.c
23 * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_nat_service.h"
29#include "gnunet-service-nat_mini.h"
30#include "nat.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
33
34/**
35 * How long do we give upnpc to create a mapping?
36 */
37#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
38
39/**
40 * How long do we give upnpc to remove a mapping?
41 */
42#define UNMAP_TIMEOUT \
43 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
44
45/**
46 * How often do we check for changes in the mapping?
47 */
48#define MAP_REFRESH_FREQ \
49 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
50
51
52/* ************************* external-ip calling ************************ */
53
54/**
55 * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
56 */
57struct GNUNET_NAT_ExternalHandle
58{
59 /**
60 * Function to call with the result.
61 */
62 GNUNET_NAT_IPCallback cb;
63
64 /**
65 * Closure for @e cb.
66 */
67 void *cb_cls;
68
69 /**
70 * Read task.
71 */
72 struct GNUNET_SCHEDULER_Task *task;
73
74 /**
75 * Handle to `external-ip` process.
76 */
77 struct GNUNET_OS_Process *eip;
78
79 /**
80 * Handle to stdout pipe of `external-ip`.
81 */
82 struct GNUNET_DISK_PipeHandle *opipe;
83
84 /**
85 * Read handle of @e opipe.
86 */
87 const struct GNUNET_DISK_FileHandle *r;
88
89 /**
90 * Number of bytes in @e buf that are valid.
91 */
92 size_t off;
93
94 /**
95 * Destination of our read operation (output of 'external-ip').
96 */
97 char buf[17];
98
99 /**
100 * Error code for better debugging and user feedback
101 */
102 enum GNUNET_NAT_StatusCode ret;
103};
104
105
106/**
107 * Read the output of `external-ip` into `buf`. When complete, parse
108 * the address and call our callback.
109 *
110 * @param cls the `struct GNUNET_NAT_ExternalHandle`
111 */
112static void
113read_external_ipv4 (void *cls)
114{
115 struct GNUNET_NAT_ExternalHandle *eh = cls;
116 ssize_t ret;
117 struct in_addr addr;
118
119 eh->task = NULL;
120 ret = GNUNET_DISK_file_read (eh->r,
121 &eh->buf[eh->off],
122 sizeof(eh->buf) - eh->off);
123 if (ret > 0)
124 {
125 /* try to read more */
126 eh->off += ret;
127 eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
128 eh->r,
129 &read_external_ipv4,
130 eh);
131 return;
132 }
133 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID;
134 if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n'))
135 {
136 eh->buf[eh->off - 1] = '\0';
137 if (1 == inet_pton (AF_INET, eh->buf, &addr))
138 {
139 if (0 == addr.s_addr)
140 eh->ret =
141 GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */
142 else
143 eh->ret = GNUNET_NAT_ERROR_SUCCESS;
144 }
145 }
146 eh->cb (eh->cb_cls,
147 (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
148 eh->ret);
149 GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh);
150}
151
152
153/**
154 * (Asynchronously) signal error invoking `external-ip` to client.
155 *
156 * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed)
157 */
158static void
159signal_external_ip_error (void *cls)
160{
161 struct GNUNET_NAT_ExternalHandle *eh = cls;
162
163 eh->task = NULL;
164 eh->cb (eh->cb_cls, NULL, eh->ret);
165 GNUNET_free (eh);
166}
167
168
169/**
170 * Try to get the external IPv4 address of this peer.
171 *
172 * @param cb function to call with result
173 * @param cb_cls closure for @a cb
174 * @return handle for cancellation (can only be used until @a cb is called), never NULL
175 */
176struct GNUNET_NAT_ExternalHandle *
177GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, void *cb_cls)
178{
179 struct GNUNET_NAT_ExternalHandle *eh;
180
181 eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle);
182 eh->cb = cb;
183 eh->cb_cls = cb_cls;
184 eh->ret = GNUNET_NAT_ERROR_SUCCESS;
185 if (GNUNET_SYSERR ==
186 GNUNET_OS_check_helper_binary ("external-ip", GNUNET_NO, NULL))
187 {
188 LOG (GNUNET_ERROR_TYPE_INFO, _ ("`external-ip' command not found\n"));
189 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND;
190 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh);
191 return eh;
192 }
193 LOG (GNUNET_ERROR_TYPE_DEBUG,
194 "Running `external-ip' to determine our external IP\n");
195 eh->opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
196 if (NULL == eh->opipe)
197 {
198 eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
199 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh);
200 return eh;
201 }
202 eh->eip = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE,
203 NULL,
204 eh->opipe,
205 NULL,
206 "external-ip",
207 "external-ip",
208 NULL);
209 if (NULL == eh->eip)
210 {
211 GNUNET_DISK_pipe_close (eh->opipe);
212 eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED;
213 eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh);
214 return eh;
215 }
216 GNUNET_DISK_pipe_close_end (eh->opipe, GNUNET_DISK_PIPE_END_WRITE);
217 eh->r = GNUNET_DISK_pipe_handle (eh->opipe, GNUNET_DISK_PIPE_END_READ);
218 eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
219 eh->r,
220 &read_external_ipv4,
221 eh);
222 return eh;
223}
224
225
226/**
227 * Cancel operation.
228 *
229 * @param eh operation to cancel
230 */
231void
232GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh)
233{
234 if (NULL != eh->eip)
235 {
236 (void) GNUNET_OS_process_kill (eh->eip, SIGKILL);
237 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (eh->eip));
238 GNUNET_OS_process_destroy (eh->eip);
239 }
240 if (NULL != eh->opipe)
241 {
242 GNUNET_DISK_pipe_close (eh->opipe);
243 eh->opipe = NULL;
244 }
245 if (NULL != eh->task)
246 {
247 GNUNET_SCHEDULER_cancel (eh->task);
248 eh->task = NULL;
249 }
250 GNUNET_free (eh);
251}
252
253
254/* ************************* upnpc calling ************************ */
255
256
257/**
258 * Handle to a mapping created with upnpc.
259 */
260struct GNUNET_NAT_MiniHandle
261{
262 /**
263 * Function to call on mapping changes.
264 */
265 GNUNET_NAT_MiniAddressCallback ac;
266
267 /**
268 * Closure for @e ac.
269 */
270 void *ac_cls;
271
272 /**
273 * Command used to install the map.
274 */
275 struct GNUNET_OS_CommandHandle *map_cmd;
276
277 /**
278 * Command used to refresh our map information.
279 */
280 struct GNUNET_OS_CommandHandle *refresh_cmd;
281
282 /**
283 * Command used to remove the mapping.
284 */
285 struct GNUNET_OS_CommandHandle *unmap_cmd;
286
287 /**
288 * Our current external mapping (if we have one).
289 */
290 struct sockaddr_in current_addr;
291
292 /**
293 * We check the mapping periodically to see if it
294 * still works. This task triggers the check.
295 */
296 struct GNUNET_SCHEDULER_Task *refresh_task;
297
298 /**
299 * Are we mapping TCP or UDP?
300 */
301 int is_tcp;
302
303 /**
304 * Did we succeed with creating a mapping?
305 */
306 int did_map;
307
308 /**
309 * Did we find our mapping during refresh scan?
310 */
311 int found;
312
313 /**
314 * Which port are we mapping?
315 */
316 uint16_t port;
317};
318
319
320/**
321 * Run "upnpc -l" to find out if our mapping changed.
322 *
323 * @param cls the `struct GNUNET_NAT_MiniHandle`
324 */
325static void
326do_refresh (void *cls);
327
328
329/**
330 * Process the output from the "upnpc -r" command.
331 *
332 * @param cls the `struct GNUNET_NAT_MiniHandle`
333 * @param line line of output, NULL at the end
334 */
335static void
336process_map_output (void *cls, const char *line);
337
338
339/**
340 * Run "upnpc -r" to map our internal port.
341 *
342 * @param mini our handle
343 */
344static void
345run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini)
346{
347 char pstr[6];
348
349 GNUNET_snprintf (pstr, sizeof(pstr), "%u", (unsigned int) mini->port);
350 mini->map_cmd = GNUNET_OS_command_run (&process_map_output,
351 mini,
352 MAP_TIMEOUT,
353 "upnpc",
354 "upnpc",
355 "-r",
356 pstr,
357 mini->is_tcp ? "tcp" : "udp",
358 NULL);
359 if (NULL == mini->map_cmd)
360 {
361 mini->ac (mini->ac_cls,
362 GNUNET_SYSERR,
363 NULL,
364 0,
365 GNUNET_NAT_ERROR_UPNPC_FAILED);
366 return;
367 }
368}
369
370
371/**
372 * Process the output from "upnpc -l" to see if our
373 * external mapping changed. If so, do the notifications.
374 *
375 * @param cls the `struct GNUNET_NAT_MiniHandle`
376 * @param line line of output, NULL at the end
377 */
378static void
379process_refresh_output (void *cls, const char *line)
380{
381 struct GNUNET_NAT_MiniHandle *mini = cls;
382 char pstr[9];
383 const char *s;
384 unsigned int nport;
385 struct in_addr exip;
386
387 if (NULL == line)
388 {
389 GNUNET_OS_command_stop (mini->refresh_cmd);
390 mini->refresh_cmd = NULL;
391 if (GNUNET_NO == mini->found)
392 {
393 /* mapping disappeared, try to re-create */
394 if (GNUNET_YES == mini->did_map)
395 {
396 mini->ac (mini->ac_cls,
397 GNUNET_NO,
398 (const struct sockaddr *) &mini->current_addr,
399 sizeof(mini->current_addr),
400 GNUNET_NAT_ERROR_SUCCESS);
401 mini->did_map = GNUNET_NO;
402 }
403 run_upnpc_r (mini);
404 }
405 return;
406 }
407 if (! mini->did_map)
408 return; /* never mapped, won't find our mapping anyway */
409
410 /* we're looking for output of the form:
411 * "ExternalIPAddress = 12.134.41.124" */
412
413 s = strstr (line, "ExternalIPAddress = ");
414 if (NULL != s)
415 {
416 s += strlen ("ExternalIPAddress = ");
417 if (1 != inet_pton (AF_INET, s, &exip))
418 return; /* skip */
419 if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
420 return; /* no change */
421 /* update mapping */
422 mini->ac (mini->ac_cls,
423 GNUNET_NO,
424 (const struct sockaddr *) &mini->current_addr,
425 sizeof(mini->current_addr),
426 GNUNET_NAT_ERROR_SUCCESS);
427 mini->current_addr.sin_addr = exip;
428 mini->ac (mini->ac_cls,
429 GNUNET_YES,
430 (const struct sockaddr *) &mini->current_addr,
431 sizeof(mini->current_addr),
432 GNUNET_NAT_ERROR_SUCCESS);
433 return;
434 }
435 /*
436 * we're looking for output of the form:
437 *
438 * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
439 * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
440 *
441 * the pattern we look for is:
442 *
443 * "%s TCP PORT->STRING:OURPORT *" or
444 * "%s UDP PORT->STRING:OURPORT *"
445 */GNUNET_snprintf (pstr, sizeof(pstr), ":%u ", mini->port);
446 if (NULL == (s = strstr (line, "->")))
447 return; /* skip */
448 if (NULL == strstr (s, pstr))
449 return; /* skip */
450 if (1 != sscanf (line,
451 (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s"
452 : "%*u UDP %u->%*s:%*u %*s",
453 &nport))
454 return; /* skip */
455 mini->found = GNUNET_YES;
456 if (nport == ntohs (mini->current_addr.sin_port))
457 return; /* no change */
458
459 /* external port changed, update mapping */
460 mini->ac (mini->ac_cls,
461 GNUNET_NO,
462 (const struct sockaddr *) &mini->current_addr,
463 sizeof(mini->current_addr),
464 GNUNET_NAT_ERROR_SUCCESS);
465 mini->current_addr.sin_port = htons ((uint16_t) nport);
466 mini->ac (mini->ac_cls,
467 GNUNET_YES,
468 (const struct sockaddr *) &mini->current_addr,
469 sizeof(mini->current_addr),
470 GNUNET_NAT_ERROR_SUCCESS);
471}
472
473
474/**
475 * Run "upnpc -l" to find out if our mapping changed.
476 *
477 * @param cls the 'struct GNUNET_NAT_MiniHandle'
478 */
479static void
480do_refresh (void *cls)
481{
482 struct GNUNET_NAT_MiniHandle *mini = cls;
483 int ac;
484
485 mini->refresh_task =
486 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini);
487 LOG (GNUNET_ERROR_TYPE_DEBUG,
488 "Running `upnpc' to check if our mapping still exists\n");
489 mini->found = GNUNET_NO;
490 ac = GNUNET_NO;
491 if (NULL != mini->map_cmd)
492 {
493 /* took way too long, abort it! */
494 GNUNET_OS_command_stop (mini->map_cmd);
495 mini->map_cmd = NULL;
496 ac = GNUNET_YES;
497 }
498 if (NULL != mini->refresh_cmd)
499 {
500 /* took way too long, abort it! */
501 GNUNET_OS_command_stop (mini->refresh_cmd);
502 mini->refresh_cmd = NULL;
503 ac = GNUNET_YES;
504 }
505 mini->refresh_cmd = GNUNET_OS_command_run (&process_refresh_output,
506 mini,
507 MAP_TIMEOUT,
508 "upnpc",
509 "upnpc",
510 "-l",
511 NULL);
512 if (GNUNET_YES == ac)
513 mini->ac (mini->ac_cls,
514 GNUNET_SYSERR,
515 NULL,
516 0,
517 GNUNET_NAT_ERROR_UPNPC_TIMEOUT);
518}
519
520
521/**
522 * Process the output from the 'upnpc -r' command.
523 *
524 * @param cls the `struct GNUNET_NAT_MiniHandle`
525 * @param line line of output, NULL at the end
526 */
527static void
528process_map_output (void *cls, const char *line)
529{
530 struct GNUNET_NAT_MiniHandle *mini = cls;
531 const char *ipaddr;
532 char *ipa;
533 const char *pstr;
534 unsigned int port;
535
536 if (NULL == line)
537 {
538 GNUNET_OS_command_stop (mini->map_cmd);
539 mini->map_cmd = NULL;
540 if (GNUNET_YES != mini->did_map)
541 mini->ac (mini->ac_cls,
542 GNUNET_SYSERR,
543 NULL,
544 0,
545 GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
546 if (NULL == mini->refresh_task)
547 mini->refresh_task =
548 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini);
549 return;
550 }
551 /*
552 * The upnpc output we're after looks like this:
553 *
554 * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
555 */if ((NULL == (ipaddr = strstr (line, " "))) ||
556 (NULL == (pstr = strstr (ipaddr, ":"))) ||
557 (1 != sscanf (pstr + 1, "%u", &port)))
558 {
559 return; /* skip line */
560 }
561 ipa = GNUNET_strdup (ipaddr + 1);
562 strstr (ipa, ":")[0] = '\0';
563 if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
564 {
565 GNUNET_free (ipa);
566 return; /* skip line */
567 }
568 GNUNET_free (ipa);
569
570 mini->current_addr.sin_port = htons (port);
571 mini->current_addr.sin_family = AF_INET;
572#if HAVE_SOCKADDR_IN_SIN_LEN
573 mini->current_addr.sin_len = sizeof(struct sockaddr_in);
574#endif
575 mini->did_map = GNUNET_YES;
576 mini->ac (mini->ac_cls,
577 GNUNET_YES,
578 (const struct sockaddr *) &mini->current_addr,
579 sizeof(mini->current_addr),
580 GNUNET_NAT_ERROR_SUCCESS);
581}
582
583
584/**
585 * Start mapping the given port using (mini)upnpc. This function
586 * should typically not be used directly (it is used within the
587 * general-purpose #GNUNET_NAT_register() code). However, it can be
588 * used if specifically UPnP-based NAT traversal is to be used or
589 * tested.
590 *
591 * @param port port to map
592 * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
593 * @param ac function to call with mapping result
594 * @param ac_cls closure for @a ac
595 * @return NULL on error (no 'upnpc' installed)
596 */
597struct GNUNET_NAT_MiniHandle *
598GNUNET_NAT_mini_map_start (uint16_t port,
599 int is_tcp,
600 GNUNET_NAT_MiniAddressCallback ac,
601 void *ac_cls)
602{
603 struct GNUNET_NAT_MiniHandle *ret;
604
605 if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL))
606 {
607 LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n"));
608 ac (ac_cls, GNUNET_SYSERR, NULL, 0, GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
609 return NULL;
610 }
611 LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n");
612 ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
613 ret->ac = ac;
614 ret->ac_cls = ac_cls;
615 ret->is_tcp = is_tcp;
616 ret->port = port;
617 ret->refresh_task =
618 GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, ret);
619 run_upnpc_r (ret);
620 return ret;
621}
622
623
624/**
625 * Process output from our 'unmap' command.
626 *
627 * @param cls the `struct GNUNET_NAT_MiniHandle`
628 * @param line line of output, NULL at the end
629 */
630static void
631process_unmap_output (void *cls, const char *line)
632{
633 struct GNUNET_NAT_MiniHandle *mini = cls;
634
635 if (NULL == line)
636 {
637 LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n");
638 GNUNET_OS_command_stop (mini->unmap_cmd);
639 mini->unmap_cmd = NULL;
640 GNUNET_free (mini);
641 return;
642 }
643 /* we don't really care about the output... */
644}
645
646
647/**
648 * Remove a mapping created with (mini)upnpc. Calling
649 * this function will give 'upnpc' 1s to remove tha mapping,
650 * so while this function is non-blocking, a task will be
651 * left with the scheduler for up to 1s past this call.
652 *
653 * @param mini the handle
654 */
655void
656GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
657{
658 char pstr[6];
659
660 if (NULL != mini->refresh_task)
661 {
662 GNUNET_SCHEDULER_cancel (mini->refresh_task);
663 mini->refresh_task = NULL;
664 }
665 if (NULL != mini->refresh_cmd)
666 {
667 GNUNET_OS_command_stop (mini->refresh_cmd);
668 mini->refresh_cmd = NULL;
669 }
670 if (NULL != mini->map_cmd)
671 {
672 GNUNET_OS_command_stop (mini->map_cmd);
673 mini->map_cmd = NULL;
674 }
675 if (GNUNET_NO == mini->did_map)
676 {
677 GNUNET_free (mini);
678 return;
679 }
680 mini->ac (mini->ac_cls,
681 GNUNET_NO,
682 (const struct sockaddr *) &mini->current_addr,
683 sizeof(mini->current_addr),
684 GNUNET_NAT_ERROR_SUCCESS);
685 /* Note: oddly enough, deletion uses the external port whereas
686 * addition uses the internal port; this rarely matters since they
687 * often are the same, but it might... */
688 GNUNET_snprintf (pstr,
689 sizeof(pstr),
690 "%u",
691 (unsigned int) ntohs (mini->current_addr.sin_port));
692 LOG (GNUNET_ERROR_TYPE_DEBUG,
693 "Unmapping port %u with UPnP\n",
694 ntohs (mini->current_addr.sin_port));
695 mini->unmap_cmd = GNUNET_OS_command_run (&process_unmap_output,
696 mini,
697 UNMAP_TIMEOUT,
698 "upnpc",
699 "upnpc",
700 "-d",
701 pstr,
702 mini->is_tcp ? "tcp" : "udp",
703 NULL);
704}
705
706
707/* end of gnunet-service-nat_mini.c */
diff --git a/src/nat/gnunet-service-nat_mini.h b/src/nat/gnunet-service-nat_mini.h
deleted file mode 100644
index dffc9758a..000000000
--- a/src/nat/gnunet-service-nat_mini.h
+++ /dev/null
@@ -1,128 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify 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_mini.c
23 * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_NAT_MINI_H
27#define GNUNET_SERVICE_NAT_MINI_H
28
29
30/**
31 * Signature of a callback that is given an IP address.
32 *
33 * @param cls closure
34 * @param addr the address, NULL on errors
35 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
36 */
37typedef void
38(*GNUNET_NAT_IPCallback) (void *cls,
39 const struct in_addr *addr,
40 enum GNUNET_NAT_StatusCode result);
41
42
43/**
44 * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation.
45 */
46struct GNUNET_NAT_ExternalHandle;
47
48
49/**
50 * Try to get the external IPv4 address of this peer.
51 *
52 * @param cb function to call with result
53 * @param cb_cls closure for @a cb
54 * @return handle for cancellation (can only be used until @a cb is called), NULL on error
55 */
56struct GNUNET_NAT_ExternalHandle *
57GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
58 void *cb_cls);
59
60
61/**
62 * Cancel operation.
63 *
64 * @param eh operation to cancel
65 */
66void
67GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct
68 GNUNET_NAT_ExternalHandle *eh);
69
70
71/**
72 * Handle to a mapping created with upnpc.
73 */
74struct GNUNET_NAT_MiniHandle;
75
76
77/**
78 * Signature of the callback passed to #GNUNET_NAT_register() for
79 * a function to call whenever our set of 'valid' addresses changes.
80 *
81 * @param cls closure
82 * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
83 * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
84 * @param addr either the previous or the new public IP address
85 * @param addrlen actual length of the @a addr
86 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
87 */
88typedef void
89(*GNUNET_NAT_MiniAddressCallback) (void *cls,
90 int add_remove,
91 const struct sockaddr *addr,
92 socklen_t addrlen,
93 enum GNUNET_NAT_StatusCode result);
94
95
96/**
97 * Start mapping the given port using (mini)upnpc. This function
98 * should typically not be used directly (it is used within the
99 * general-purpose #GNUNET_NAT_register() code). However, it can be
100 * used if specifically UPnP-based NAT traversal is to be used or
101 * tested.
102 *
103 * @param port port to map
104 * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
105 * @param ac function to call with mapping result
106 * @param ac_cls closure for @a ac
107 * @return NULL on error
108 */
109struct GNUNET_NAT_MiniHandle *
110GNUNET_NAT_mini_map_start (uint16_t port,
111 int is_tcp,
112 GNUNET_NAT_MiniAddressCallback ac,
113 void *ac_cls);
114
115
116/**
117 * Remove a mapping created with (mini)upnpc. Calling
118 * this function will give 'upnpc' 1s to remove the mapping,
119 * so while this function is non-blocking, a task will be
120 * left with the scheduler for up to 1s past this call.
121 *
122 * @param mini the handle
123 */
124void
125GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini);
126
127
128#endif
diff --git a/src/nat/gnunet-service-nat_stun.c b/src/nat/gnunet-service-nat_stun.c
deleted file mode 100644
index 203728ebf..000000000
--- a/src/nat/gnunet-service-nat_stun.c
+++ /dev/null
@@ -1,215 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016 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 * This code provides some support for doing STUN transactions. We
22 * receive the simplest possible packet as the STUN server and try
23 * to respond properly.
24 *
25 * All STUN packets start with a simple header made of a type,
26 * length (excluding the header) and a 16-byte random transaction id.
27 * Following the header we may have zero or more attributes, each
28 * structured as a type, length and a value (whose format depends
29 * on the type, but often contains addresses).
30 * Of course all fields are in network format.
31 *
32 * This code was based on ministun.c.
33 *
34 * @file nat/gnunet-service-nat_stun.c
35 * @brief Functions for STUN functionality
36 * @author Bruno Souza Cabral
37 */
38
39#include "platform.h"
40#include "gnunet_util_lib.h"
41#include "nat_stun.h"
42
43#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
44
45
46/**
47 * Context for #stun_get_mapped().
48 * Used to store state across processing attributes.
49 */
50struct StunState
51{
52 uint16_t attr;
53};
54
55
56/**
57 * Extract the STUN_MAPPED_ADDRESS from the stun response.
58 * This is used as a callback for stun_handle_response
59 * when called from stun_request.
60 *
61 * @param[out] st pointer where we will set the type
62 * @param attr received stun attribute
63 * @param magic Magic cookie
64 * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port
65 * @return #GNUNET_OK if @a arg was initialized
66 */
67static int
68stun_get_mapped (struct StunState *st,
69 const struct stun_attr *attr,
70 uint32_t magic,
71 struct sockaddr_in *arg)
72{
73 const struct stun_addr *returned_addr;
74 struct sockaddr_in *sa = (struct sockaddr_in *) arg;
75 uint16_t type = ntohs (attr->attr);
76
77 switch (type)
78 {
79 case STUN_MAPPED_ADDRESS:
80 if ((st->attr == STUN_XOR_MAPPED_ADDRESS) ||
81 (st->attr == STUN_MS_XOR_MAPPED_ADDRESS))
82 return GNUNET_NO;
83 magic = 0;
84 break;
85
86 case STUN_MS_XOR_MAPPED_ADDRESS:
87 if (st->attr == STUN_XOR_MAPPED_ADDRESS)
88 return GNUNET_NO;
89 break;
90
91 case STUN_XOR_MAPPED_ADDRESS:
92 break;
93
94 default:
95 return GNUNET_NO;
96 }
97
98 if (ntohs (attr->len) < sizeof(struct stun_addr))
99 return GNUNET_NO;
100 returned_addr = (const struct stun_addr *) (attr + 1);
101 if (AF_INET != returned_addr->family)
102 return GNUNET_NO;
103 st->attr = type;
104 sa->sin_family = AF_INET;
105 sa->sin_port = returned_addr->port ^ htons (ntohl (magic) >> 16);
106 sa->sin_addr.s_addr = returned_addr->addr ^ magic;
107 return GNUNET_OK;
108}
109
110
111/**
112 * Handle an incoming STUN response. Do some basic sanity checks on
113 * packet size and content, try to extract information.
114 * At the moment this only processes BIND requests,
115 * and returns the externally visible address of the original
116 * request.
117 *
118 * @param data the packet
119 * @param len the length of the packet in @a data
120 * @param[out] arg sockaddr_in where we will set our discovered address
121 * @return #GNUNET_OK on success,
122 * #GNUNET_NO if the packet is invalid (not a stun packet)
123 */
124int
125GNUNET_NAT_stun_handle_packet_ (const void *data,
126 size_t len,
127 struct sockaddr_in *arg)
128{
129 const struct stun_header *hdr;
130 const struct stun_attr *attr;
131 struct StunState st;
132 uint32_t advertised_message_size;
133 uint32_t message_magic_cookie;
134 int ret = GNUNET_SYSERR;
135
136 /* On entry, 'len' is the length of the UDP payload. After the
137 * initial checks it becomes the size of unprocessed options,
138 * while 'data' is advanced accordingly.
139 */
140 if (len < sizeof(struct stun_header))
141 {
142 LOG (GNUNET_ERROR_TYPE_DEBUG,
143 "Packet too short to be a STUN packet\n");
144 return GNUNET_NO;
145 }
146 hdr = data;
147 /* Skip header as it is already in hdr */
148 len -= sizeof(struct stun_header);
149 data += sizeof(struct stun_header);
150
151 /* len as advertised in the message */
152 advertised_message_size = ntohs (hdr->msglen);
153 message_magic_cookie = ntohl (hdr->magic);
154 /* Compare if the cookie match */
155 if (STUN_MAGIC_COOKIE != message_magic_cookie)
156 {
157 LOG (GNUNET_ERROR_TYPE_DEBUG,
158 "Invalid magic cookie for STUN packet\n");
159 return GNUNET_NO;
160 }
161
162 LOG (GNUNET_ERROR_TYPE_INFO,
163 "STUN Packet, msg %s (%04x), length: %d\n",
164 stun_msg2str (ntohs (hdr->msgtype)),
165 ntohs (hdr->msgtype),
166 advertised_message_size);
167 if (advertised_message_size > len)
168 {
169 LOG (GNUNET_ERROR_TYPE_INFO,
170 "Scrambled STUN packet length (got %d, expecting %d)\n",
171 advertised_message_size,
172 (int) len);
173 return GNUNET_NO;
174 }
175 len = advertised_message_size;
176 memset (&st, 0, sizeof(st));
177
178 while (len > 0)
179 {
180 if (len < sizeof(struct stun_attr))
181 {
182 LOG (GNUNET_ERROR_TYPE_INFO,
183 "Attribute too short (got %d, expecting %d)\n",
184 (int) len,
185 (int) sizeof(struct stun_attr));
186 break;
187 }
188 attr = (const struct stun_attr *) data;
189
190 /* compute total attribute length */
191 advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
192
193 /* Check if we still have space in our buffer */
194 if (advertised_message_size > len)
195 {
196 LOG (GNUNET_ERROR_TYPE_INFO,
197 "Inconsistent attribute (length %d exceeds remaining msg len %d)\n",
198 advertised_message_size,
199 (int) len);
200 break;
201 }
202 if (GNUNET_OK ==
203 stun_get_mapped (&st,
204 attr,
205 hdr->magic,
206 arg))
207 ret = GNUNET_OK;
208 data += advertised_message_size;
209 len -= advertised_message_size;
210 }
211 return ret;
212}
213
214
215/* end of gnunet-service-nat_stun.c */
diff --git a/src/nat/gnunet-service-nat_stun.h b/src/nat/gnunet-service-nat_stun.h
deleted file mode 100644
index 912fc3b46..000000000
--- a/src/nat/gnunet-service-nat_stun.h
+++ /dev/null
@@ -1,61 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016 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 * This code provides some support for doing STUN transactions. We
22 * receive the simplest possible packet as the STUN server and try
23 * to respond properly.
24 *
25 * All STUN packets start with a simple header made of a type,
26 * length (excluding the header) and a 16-byte random transaction id.
27 * Following the header we may have zero or more attributes, each
28 * structured as a type, length and a value (whose format depends
29 * on the type, but often contains addresses).
30 * Of course all fields are in network format.
31 *
32 * This code was based on ministun.c.
33 *
34 * @file nat/gnunet-service-nat_stun.h
35 * @brief Functions for STUN functionality
36 * @author Bruno Souza Cabral
37 */
38#ifndef GNUNET_SERVICE_NAT_STUN_H
39#define GNUNET_SERVICE_NAT_STUN_H
40
41#include "platform.h"
42
43/**
44 * Handle an incoming STUN response. Do some basic sanity checks on
45 * packet size and content, try to extract information.
46 * At the moment this only processes BIND requests,
47 * and returns the externally visible address of the original
48 * request.
49 *
50 * @param data the packet
51 * @param len the length of the packet in @a data
52 * @param[out] arg sockaddr_in where we will set our discovered address
53 * @return #GNUNET_OK on success,
54 * #GNUNET_NO if the packet is invalid (not a stun packet)
55 */
56int
57GNUNET_NAT_stun_handle_packet_ (const void *data,
58 size_t len,
59 struct sockaddr_in *arg);
60
61#endif
diff --git a/src/nat/nat.conf.in b/src/nat/nat.conf.in
deleted file mode 100644
index 4c068c394..000000000
--- a/src/nat/nat.conf.in
+++ /dev/null
@@ -1,43 +0,0 @@
1[nat]
2START_ON_DEMAND = @START_ON_DEMAND@
3@UNIXONLY@ PORT = 2121
4HOSTNAME = localhost
5BINARY = gnunet-service-nat
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
11
12# Enable UPNP by default?
13ENABLE_UPNP = YES
14
15# Enable scanning for all system IP addresses?
16ENABLE_IPSCAN = YES
17
18# Disable IPv6 support
19# FIXME: move entirely to transport plugins!
20DISABLEV6 = NO
21
22# How often do we query the DNS resolver
23# for our hostname (to get our own IP)
24HOSTNAME_DNS_FREQUENCY = 20 min
25
26# How often do we iterate over our
27# network interfaces to check for changes
28# in our IP address?
29IFC_SCAN_FREQUENCY = 15 min
30
31# How often do we query the DNS resolver
32# for our hostname (to get our own IP)
33DYNDNS_FREQUENCY = 7 min
34
35# SHOULD USE STUN ?
36USE_STUN = YES
37STUN_FREQUENCY = 5 min
38# Default list of stun servers
39STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:3478
40
41# After how long do we consider STUN data stale?
42STUN_STALE = 60 min
43
diff --git a/src/nat/nat.h b/src/nat/nat.h
deleted file mode 100644
index 1d8648aaf..000000000
--- a/src/nat/nat.h
+++ /dev/null
@@ -1,223 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 2016 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 src/nat/nat.h
23 * @brief Messages for interaction with gnunet-nat-server and gnunet-nat-service
24 * @author Christian Grothoff
25 *
26 */
27#ifndef NAT_H
28#define NAT_H
29#include "gnunet_util_lib.h"
30
31
32GNUNET_NETWORK_STRUCT_BEGIN
33
34/**
35 * Request to test NAT traversal, sent to the gnunet-nat-server
36 * (not the service!).
37 */
38struct GNUNET_NAT_TestMessage
39{
40 /**
41 * Header with type #GNUNET_MESSAGE_TYPE_NAT_TEST
42 */
43 struct GNUNET_MessageHeader header;
44
45 /**
46 * IPv4 target IP address
47 */
48 uint32_t dst_ipv4;
49
50 /**
51 * Port to use, 0 to send dummy ICMP response.
52 */
53 uint16_t dport;
54
55 /**
56 * Data to send OR advertised-port (in NBO) to use for dummy ICMP.
57 */
58 uint16_t data;
59
60 /**
61 * #GNUNET_YES for TCP, #GNUNET_NO for UDP.
62 */
63 int32_t is_tcp;
64};
65
66
67/**
68 * Flags specifying the events this client would be
69 * interested in being told about.
70 */
71enum GNUNET_NAT_RegisterFlags
72{
73 /**
74 * This client does not want any notifications.
75 */
76 GNUNET_NAT_RF_NONE = 0,
77
78 /**
79 * This client wants to be informed about changes to our
80 * applicable addresses.
81 */
82 GNUNET_NAT_RF_ADDRESSES = 1,
83
84 /**
85 * This client supports address reversal.
86 */
87 GNUNET_NAT_RF_REVERSAL = 2
88};
89
90
91/**
92 * Message sent by a client to register with its addresses.
93 */
94struct GNUNET_NAT_RegisterMessage
95{
96 /**
97 * Header with type #GNUNET_MESSAGE_TYPE_NAT_REGISTER
98 */
99 struct GNUNET_MessageHeader header;
100
101 /**
102 * An `enum GNUNET_NAT_RegisterFlags`.
103 */
104 uint8_t flags;
105
106 /**
107 * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
108 */
109 uint8_t proto;
110
111 /**
112 * Number of bytes in the string that follow which
113 * specifies a section name in the configuration.
114 */
115 uint16_t str_len GNUNET_PACKED;
116
117 /**
118 * Number of addresses that this service is bound to that follow.
119 * Given as an array of "struct sockaddr" entries, the size of
120 * each entry being determined by the "sa_family" at the beginning.
121 */
122 uint16_t num_addrs GNUNET_PACKED;
123
124 /* Followed by @e num_addrs addresses of type 'struct
125 sockaddr' */
126
127 /* Followed by @e str_len section name to use for options */
128};
129
130
131/**
132 * Client telling the service to (possibly) handle a STUN message.
133 */
134struct GNUNET_NAT_HandleStunMessage
135{
136 /**
137 * Header with type #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN
138 */
139 struct GNUNET_MessageHeader header;
140
141 /**
142 * Size of the sender address included, in NBO.
143 */
144 uint16_t sender_addr_size;
145
146 /**
147 * Number of bytes of payload included, in NBO.
148 */
149 uint16_t payload_size;
150
151 /* followed by a `struct sockaddr` of @e sender_addr_size bytes */
152
153 /* followed by payload with @e payload_size bytes */
154};
155
156
157/**
158 * Client asking the service to initiate connection reversal.
159 */
160struct GNUNET_NAT_RequestConnectionReversalMessage
161{
162 /**
163 * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL
164 */
165 struct GNUNET_MessageHeader header;
166
167 /**
168 * Size of the local address included, in NBO.
169 */
170 uint16_t local_addr_size;
171
172 /**
173 * Size of the remote address included, in NBO.
174 */
175 uint16_t remote_addr_size;
176
177 /* followed by a `struct sockaddr` of @e local_addr_size bytes */
178
179 /* followed by a `struct sockaddr` of @e remote_addr_size bytes */
180};
181
182
183/**
184 * Service telling a client that connection reversal was requested.
185 */
186struct GNUNET_NAT_ConnectionReversalRequestedMessage
187{
188 /**
189 * Header with type #GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED
190 */
191 struct GNUNET_MessageHeader header;
192
193 /* followed by a `struct sockaddr_in` */
194};
195
196
197/**
198 * Service notifying the client about changes in the set of
199 * addresses it has.
200 */
201struct GNUNET_NAT_AddressChangeNotificationMessage
202{
203 /**
204 * Header with type #GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE
205 */
206 struct GNUNET_MessageHeader header;
207
208 /**
209 * #GNUNET_YES to add, #GNUNET_NO to remove the address from the list.
210 */
211 int32_t add_remove GNUNET_PACKED;
212
213 /**
214 * Type of the address, an `enum GNUNET_NAT_AddressClass` in NBO.
215 */
216 uint32_t addr_class GNUNET_PACKED;
217 /* followed by a `struct sockaddr` */
218};
219
220
221GNUNET_NETWORK_STRUCT_END
222
223#endif
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c
deleted file mode 100644
index 20e7b6ec6..000000000
--- a/src/nat/nat_api.c
+++ /dev/null
@@ -1,718 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 * @author Milan Bouchet-Valat
24 *
25 * @file nat/nat_api.c
26 * Service for handling UPnP and NAT-PMP port forwarding
27 * and external IP address retrieval
28 */
29#include "platform.h"
30#include "gnunet_nat_service.h"
31#include "nat.h"
32#include "nat_stun.h"
33
34
35/**
36 * Entry in DLL of addresses of this peer.
37 */
38struct AddrEntry
39{
40 /**
41 * DLL.
42 */
43 struct AddrEntry *next;
44
45 /**
46 * DLL.
47 */
48 struct AddrEntry *prev;
49
50 /**
51 * Place where the application can store data (on add,
52 * and retrieve on remove).
53 */
54 void *app_ctx;
55
56 /**
57 * Address class of the address.
58 */
59 enum GNUNET_NAT_AddressClass ac;
60
61 /**
62 * Number of bytes that follow.
63 */
64 socklen_t addrlen;
65};
66
67
68/**
69 * Handle for active NAT registrations.
70 */
71struct GNUNET_NAT_Handle
72{
73 /**
74 * Configuration we use.
75 */
76 const struct GNUNET_CONFIGURATION_Handle *cfg;
77
78 /**
79 * Message queue for communicating with the NAT service.
80 */
81 struct GNUNET_MQ_Handle *mq;
82
83 /**
84 * Our registration message.
85 */
86 struct GNUNET_MessageHeader *reg;
87
88 /**
89 * Head of address DLL.
90 */
91 struct AddrEntry *ae_head;
92
93 /**
94 * Tail of address DLL.
95 */
96 struct AddrEntry *ae_tail;
97
98 /**
99 * Function to call when our addresses change.
100 */
101 GNUNET_NAT_AddressCallback address_callback;
102
103 /**
104 * Function to call when another peer requests connection reversal.
105 */
106 GNUNET_NAT_ReversalCallback reversal_callback;
107
108 /**
109 * Closure for the various callbacks.
110 */
111 void *callback_cls;
112
113 /**
114 * Task scheduled to reconnect to the service.
115 */
116 struct GNUNET_SCHEDULER_Task *reconnect_task;
117
118 /**
119 * How long to wait until we reconnect.
120 */
121 struct GNUNET_TIME_Relative reconnect_delay;
122};
123
124
125/**
126 * Task to connect to the NAT service.
127 *
128 * @param cls our `struct GNUNET_NAT_Handle *`
129 */
130static void
131do_connect (void *cls);
132
133
134/**
135 * Task to connect to the NAT service.
136 *
137 * @param nh handle to reconnect
138 */
139static void
140reconnect (struct GNUNET_NAT_Handle *nh)
141{
142 struct AddrEntry *ae;
143
144 if (NULL != nh->mq)
145 {
146 GNUNET_MQ_destroy (nh->mq);
147 nh->mq = NULL;
148 }
149 while (NULL != (ae = nh->ae_head))
150 {
151 GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
152 nh->address_callback (nh->callback_cls,
153 &ae->app_ctx,
154 GNUNET_NO,
155 ae->ac,
156 (const struct sockaddr *) &ae[1],
157 ae->addrlen);
158 GNUNET_free (ae);
159 }
160 nh->reconnect_delay = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay);
161 nh->reconnect_task =
162 GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay, &do_connect, nh);
163}
164
165
166/**
167 * Check connection reversal request.
168 *
169 * @param cls our `struct GNUNET_NAT_Handle`
170 * @param crm the message
171 * @return #GNUNET_OK if @a crm is well-formed
172 */
173static int
174check_connection_reversal_request (
175 void *cls,
176 const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
177{
178 if (ntohs (crm->header.size) != sizeof(*crm) + sizeof(struct sockaddr_in))
179 {
180 GNUNET_break (0);
181 return GNUNET_SYSERR;
182 }
183 return GNUNET_OK;
184}
185
186
187/**
188 * Handle connection reversal request.
189 *
190 * @param cls our `struct GNUNET_NAT_Handle`
191 * @param crm the message
192 */
193static void
194handle_connection_reversal_request (
195 void *cls,
196 const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
197{
198 struct GNUNET_NAT_Handle *nh = cls;
199
200 nh->reversal_callback (nh->callback_cls,
201 (const struct sockaddr *) &crm[1],
202 sizeof(struct sockaddr_in));
203}
204
205
206/**
207 * Check address change notification.
208 *
209 * @param cls our `struct GNUNET_NAT_Handle`
210 * @param acn the message
211 * @return #GNUNET_OK if @a crm is well-formed
212 */
213static int
214check_address_change_notification (
215 void *cls,
216 const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
217{
218 size_t alen = ntohs (acn->header.size) - sizeof(*acn);
219
220 switch (alen)
221 {
222 case sizeof(struct sockaddr_in): {
223 const struct sockaddr_in *s4 = (const struct sockaddr_in *) &acn[1];
224 if (AF_INET != s4->sin_family)
225 {
226 GNUNET_break (0);
227 return GNUNET_SYSERR;
228 }
229 }
230 break;
231
232 case sizeof(struct sockaddr_in6): {
233 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) &acn[1];
234 if (AF_INET6 != s6->sin6_family)
235 {
236 GNUNET_break (0);
237 return GNUNET_SYSERR;
238 }
239 }
240 break;
241
242 default:
243 GNUNET_break (0);
244 return GNUNET_SYSERR;
245 }
246 return GNUNET_OK;
247}
248
249
250/**
251 * Handle connection reversal request.
252 *
253 * @param cls our `struct GNUNET_NAT_Handle`
254 * @param acn the message
255 */
256static void
257handle_address_change_notification (
258 void *cls,
259 const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
260{
261 struct GNUNET_NAT_Handle *nh = cls;
262 size_t alen = ntohs (acn->header.size) - sizeof(*acn);
263 const struct sockaddr *sa = (const struct sockaddr *) &acn[1];
264 enum GNUNET_NAT_AddressClass ac;
265 struct AddrEntry *ae;
266
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Received address change notification\n");
269 ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class);
270 if (GNUNET_YES == ntohl (acn->add_remove))
271 {
272 ae = GNUNET_malloc (sizeof(*ae) + alen);
273 ae->ac = ac;
274 ae->addrlen = alen;
275 GNUNET_memcpy (&ae[1], sa, alen);
276 GNUNET_CONTAINER_DLL_insert (nh->ae_head, nh->ae_tail, ae);
277 nh->address_callback (nh->callback_cls,
278 &ae->app_ctx,
279 ntohl (acn->add_remove),
280 ac,
281 sa,
282 alen);
283 }
284 else
285 {
286 for (ae = nh->ae_head; NULL != ae; ae = ae->next)
287 if ((ae->addrlen == alen) && (0 == memcmp (&ae[1], sa, alen)))
288 break;
289 if (NULL == ae)
290 {
291 GNUNET_break (0);
292 reconnect (nh);
293 return;
294 }
295 GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae);
296 nh->address_callback (nh->callback_cls,
297 &ae->app_ctx,
298 ntohl (acn->add_remove),
299 ac,
300 sa,
301 alen);
302 GNUNET_free (ae);
303 }
304}
305
306
307/**
308 * Handle queue errors by reconnecting to NAT.
309 *
310 * @param cls the `struct GNUNET_NAT_Handle *`
311 * @param error details about the error
312 */
313static void
314mq_error_handler (void *cls,
315 enum GNUNET_MQ_Error error)
316{
317 struct GNUNET_NAT_Handle *nh = cls;
318
319 reconnect (nh);
320}
321
322
323/**
324 * Task to connect to the NAT service.
325 *
326 * @param cls our `struct GNUNET_NAT_Handle *`
327 */
328static void
329do_connect (void *cls)
330{
331 struct GNUNET_NAT_Handle *nh = cls;
332 struct GNUNET_MQ_MessageHandler handlers[] = {
333 GNUNET_MQ_hd_var_size (
334 connection_reversal_request,
335 GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED,
336 struct GNUNET_NAT_ConnectionReversalRequestedMessage,
337 nh),
338 GNUNET_MQ_hd_var_size (
339 address_change_notification,
340 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE,
341 struct GNUNET_NAT_AddressChangeNotificationMessage,
342 nh),
343 GNUNET_MQ_handler_end ()
344 };
345 struct GNUNET_MQ_Envelope *env;
346
347 nh->reconnect_task = NULL;
348 nh->mq =
349 GNUNET_CLIENT_connect (nh->cfg,
350 "nat",
351 handlers,
352 &mq_error_handler,
353 nh);
354 if (NULL == nh->mq)
355 {
356 reconnect (nh);
357 return;
358 }
359 env = GNUNET_MQ_msg_copy (nh->reg);
360 GNUNET_MQ_send (nh->mq,
361 env);
362}
363
364
365/**
366 * Attempt to enable port redirection and detect public IP address
367 * contacting UPnP or NAT-PMP routers on the local network. Use @a
368 * addr to specify to which of the local host's addresses should the
369 * external port be mapped. The port is taken from the corresponding
370 * sockaddr_in[6] field. The NAT module should call the given @a
371 * address_callback for any 'plausible' external address.
372 *
373 * @param cfg configuration to use
374 * @param config_section name of the configuration section for optionsx
375 * @param proto protocol this is about, IPPROTO_TCP or IPPROTO_UDP
376 * @param num_addrs number of addresses in @a addrs
377 * @param addrs list of local addresses packets should be redirected to
378 * @param addrlens actual lengths of the addresses in @a addrs
379 * @param address_callback function to call every time the public IP address changes
380 * @param reversal_callback function to call if someone wants connection reversal from us,
381 * NULL if connection reversal is not supported
382 * @param callback_cls closure for callbacks
383 * @return NULL on error, otherwise handle that can be used to unregister
384 */
385struct GNUNET_NAT_Handle *
386GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg,
387 const char *config_section,
388 uint8_t proto,
389 unsigned int num_addrs,
390 const struct sockaddr **addrs,
391 const socklen_t *addrlens,
392 GNUNET_NAT_AddressCallback address_callback,
393 GNUNET_NAT_ReversalCallback reversal_callback,
394 void *callback_cls)
395{
396 struct GNUNET_NAT_Handle *nh;
397 struct GNUNET_NAT_RegisterMessage *rm;
398 size_t len;
399 size_t str_len;
400 char *off;
401
402 len = 0;
403 for (unsigned int i = 0; i < num_addrs; i++)
404 len += addrlens[i];
405 str_len = strlen (config_section) + 1;
406 len += str_len;
407 if ( (len > GNUNET_MAX_MESSAGE_SIZE - sizeof(*rm)) ||
408 (num_addrs > UINT16_MAX) ||
409 (str_len > UINT16_MAX) )
410 {
411 GNUNET_break (0);
412 return NULL;
413 }
414 rm = GNUNET_malloc (sizeof(*rm) + len);
415 rm->header.size = htons (sizeof(*rm) + len);
416 rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER);
417 rm->flags = GNUNET_NAT_RF_NONE;
418 if (NULL != address_callback)
419 rm->flags |= GNUNET_NAT_RF_ADDRESSES;
420 if (NULL != reversal_callback)
421 rm->flags |= GNUNET_NAT_RF_REVERSAL;
422 rm->proto = proto;
423 rm->str_len = htons (str_len);
424 rm->num_addrs = htons ((uint16_t) num_addrs);
425 off = (char *) &rm[1];
426 for (unsigned int i = 0; i < num_addrs; i++)
427 {
428 switch (addrs[i]->sa_family)
429 {
430 case AF_INET:
431 if (sizeof(struct sockaddr_in) != addrlens[i])
432 {
433 GNUNET_break (0);
434 GNUNET_free (rm);
435 return NULL;
436 }
437 break;
438
439 case AF_INET6:
440 if (sizeof(struct sockaddr_in6) != addrlens[i])
441 {
442 GNUNET_break (0);
443 GNUNET_free (rm);
444 return NULL;
445 }
446 break;
447
448#if AF_UNIX
449 case AF_UNIX:
450 if (sizeof(struct sockaddr_un) != addrlens[i])
451 {
452 GNUNET_break (0);
453 GNUNET_free (rm);
454 return NULL;
455 }
456 break;
457#endif
458 default:
459 GNUNET_break (0);
460 GNUNET_free (rm);
461 return NULL;
462 }
463 GNUNET_memcpy (off, addrs[i], addrlens[i]);
464 off += addrlens[i];
465 }
466 GNUNET_memcpy (off, config_section, str_len);
467
468 nh = GNUNET_new (struct GNUNET_NAT_Handle);
469 nh->reg = &rm->header;
470 nh->cfg = cfg;
471 nh->address_callback = address_callback;
472 nh->reversal_callback = reversal_callback;
473 nh->callback_cls = callback_cls;
474 do_connect (nh);
475 return nh;
476}
477
478
479/**
480 * Check if an incoming message is a STUN message.
481 *
482 * @param data the packet
483 * @param len the length of the packet in @a data
484 * @return #GNUNET_YES if @a data is a STUN packet,
485 * #GNUNET_NO if the packet is invalid (not a stun packet)
486 */
487static enum GNUNET_GenericReturnValue
488test_stun_packet (const void *data, size_t len)
489{
490 const struct stun_header *hdr;
491 const struct stun_attr *attr;
492 uint32_t advertised_message_size;
493 uint32_t message_magic_cookie;
494
495 /* On entry, 'len' is the length of the UDP payload. After the
496 * initial checks it becomes the size of unprocessed options,
497 * while 'data' is advanced accordingly.
498 */
499 if (len < sizeof(struct stun_header))
500 {
501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 "STUN packet too short (only %d, wanting at least %d)\n",
503 (int) len,
504 (int) sizeof(struct stun_header));
505 return GNUNET_NO;
506 }
507 hdr = (const struct stun_header *) data;
508 /* Skip header as it is already in hdr */
509 len -= sizeof(struct stun_header);
510 data += sizeof(struct stun_header);
511
512 /* len as advertised in the message */
513 advertised_message_size = ntohs (hdr->msglen);
514
515 message_magic_cookie = ntohl (hdr->magic);
516 /* Compare if the cookie match */
517 if (STUN_MAGIC_COOKIE != message_magic_cookie)
518 {
519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid magic cookie for STUN\n");
520 return GNUNET_NO;
521 }
522
523 if (advertised_message_size > len)
524 {
525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526 "Scrambled STUN packet length (got %d, expecting %d)\n",
527 advertised_message_size,
528 (int) len);
529 return GNUNET_NO;
530 }
531 len = advertised_message_size;
532 while (len > 0)
533 {
534 if (len < sizeof(struct stun_attr))
535 {
536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537 "Attribute too short in STUN packet (got %d, expecting %d)\n",
538 (int) len,
539 (int) sizeof(struct stun_attr));
540 return GNUNET_NO;
541 }
542 attr = (const struct stun_attr *) data;
543
544 /* compute total attribute length */
545 advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
546
547 /* Check if we still have space in our buffer */
548 if (advertised_message_size > len)
549 {
550 GNUNET_log (
551 GNUNET_ERROR_TYPE_DEBUG,
552 "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n",
553 advertised_message_size,
554 (int) len);
555 return GNUNET_NO;
556 }
557 data += advertised_message_size;
558 len -= advertised_message_size;
559 }
560 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561 "STUN Packet, msg %04x, length: %d\n",
562 ntohs (hdr->msgtype),
563 advertised_message_size);
564 return GNUNET_OK;
565}
566
567
568/**
569 * Handle an incoming STUN message. This function is useful as
570 * some GNUnet service may be listening on a UDP port and might
571 * thus receive STUN messages while trying to receive other data.
572 * In this case, this function can be used to process replies
573 * to STUN requests.
574 *
575 * The function does some basic sanity checks on packet size and
576 * content, try to extract a bit of information.
577 *
578 * At the moment this only processes BIND requests, and returns the
579 * externally visible address of the request to the rest of the
580 * NAT logic.
581 *
582 * @param nh handle to the NAT service
583 * @param sender_addr address from which we got @a data
584 * @param sender_addr_len number of bytes in @a sender_addr
585 * @param data the packet
586 * @param data_size number of bytes in @a data
587 * @return #GNUNET_OK on success
588 * #GNUNET_NO if the packet is not a STUN packet
589 * #GNUNET_SYSERR on internal error handling the packet
590 */
591int
592GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh,
593 const struct sockaddr *sender_addr,
594 size_t sender_addr_len,
595 const void *data,
596 size_t data_size)
597{
598 struct GNUNET_MQ_Envelope *env;
599 struct GNUNET_NAT_HandleStunMessage *hsn;
600 char *buf;
601
602 if (GNUNET_YES != test_stun_packet (data, data_size))
603 return GNUNET_NO;
604 if (NULL == nh->mq)
605 return GNUNET_SYSERR;
606 env = GNUNET_MQ_msg_extra (hsn,
607 data_size + sender_addr_len,
608 GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN);
609 hsn->sender_addr_size = htons ((uint16_t) sender_addr_len);
610 hsn->payload_size = htons ((uint16_t) data_size);
611 buf = (char *) &hsn[1];
612 GNUNET_memcpy (buf, sender_addr, sender_addr_len);
613 buf += sender_addr_len;
614 GNUNET_memcpy (buf, data, data_size);
615 GNUNET_MQ_send (nh->mq, env);
616 return GNUNET_OK;
617}
618
619
620/**
621 * Test if the given address is (currently) a plausible IP address for
622 * this peer. Mostly a convenience function so that clients do not
623 * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback
624 * has returned so far.
625 *
626 * @param nh the handle returned by register
627 * @param addr IP address to test (IPv4 or IPv6)
628 * @param addrlen number of bytes in @a addr
629 * @return #GNUNET_YES if the address is plausible,
630 * #GNUNET_NO if the address is not plausible,
631 * #GNUNET_SYSERR if the address is malformed
632 */
633int
634GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh,
635 const void *addr,
636 socklen_t addrlen)
637{
638 struct AddrEntry *ae;
639
640 if ((addrlen != sizeof(struct sockaddr_in)) &&
641 (addrlen != sizeof(struct sockaddr_in6)))
642 {
643 GNUNET_break (0);
644 return GNUNET_SYSERR;
645 }
646 for (ae = nh->ae_head; NULL != ae; ae = ae->next)
647 if ((addrlen == ae->addrlen) && (0 == memcmp (addr, &ae[1], addrlen)))
648 return GNUNET_YES;
649 return GNUNET_NO;
650}
651
652
653/**
654 * We learned about a peer (possibly behind NAT) so run the
655 * gnunet-nat-client to send dummy ICMP responses to cause
656 * that peer to connect to us (connection reversal).
657 *
658 * @param nh handle (used for configuration)
659 * @param local_sa our local address of the peer (IPv4-only)
660 * @param remote_sa the remote address of the peer (IPv4-only)
661 * @return #GNUNET_SYSERR on error,
662 * #GNUNET_NO if connection reversal is unavailable,
663 * #GNUNET_OK otherwise (presumably in progress)
664 */
665int
666GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh,
667 const struct sockaddr_in *local_sa,
668 const struct sockaddr_in *remote_sa)
669{
670 struct GNUNET_MQ_Envelope *env;
671 struct GNUNET_NAT_RequestConnectionReversalMessage *req;
672 char *buf;
673
674 if (NULL == nh->mq)
675 return GNUNET_SYSERR;
676 GNUNET_break (AF_INET == local_sa->sin_family);
677 GNUNET_break (AF_INET == remote_sa->sin_family);
678 env =
679 GNUNET_MQ_msg_extra (req,
680 2 * sizeof(struct sockaddr_in),
681 GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL);
682 req->local_addr_size = htons (sizeof(struct sockaddr_in));
683 req->remote_addr_size = htons (sizeof(struct sockaddr_in));
684 buf = (char *) &req[1];
685 GNUNET_memcpy (buf, local_sa, sizeof(struct sockaddr_in));
686 buf += sizeof(struct sockaddr_in);
687 GNUNET_memcpy (buf, remote_sa, sizeof(struct sockaddr_in));
688 GNUNET_MQ_send (nh->mq, env);
689 return GNUNET_OK;
690}
691
692
693/**
694 * Stop port redirection and public IP address detection for the given
695 * handle. This frees the handle, after having sent the needed
696 * commands to close open ports.
697 *
698 * @param nh the handle to stop
699 */
700void
701GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh)
702{
703 if (NULL != nh->mq)
704 {
705 GNUNET_MQ_destroy (nh->mq);
706 nh->mq = NULL;
707 }
708 if (NULL != nh->reconnect_task)
709 {
710 GNUNET_SCHEDULER_cancel (nh->reconnect_task);
711 nh->reconnect_task = NULL;
712 }
713 GNUNET_free (nh->reg);
714 GNUNET_free (nh);
715}
716
717
718/* end of nat_api.c */
diff --git a/src/nat/nat_api_stun.c b/src/nat/nat_api_stun.c
deleted file mode 100644
index 0bc2c5fbd..000000000
--- a/src/nat/nat_api_stun.c
+++ /dev/null
@@ -1,259 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016 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 * This code provides some support for doing STUN transactions.
22 * We send simplest possible packet ia REQUEST with BIND to a STUN server.
23 *
24 * All STUN packets start with a simple header made of a type,
25 * length (excluding the header) and a 16-byte random transaction id.
26 * Following the header we may have zero or more attributes, each
27 * structured as a type, length and a value (whose format depends
28 * on the type, but often contains addresses).
29 * Of course all fields are in network format.
30 *
31 * This code was based on ministun.c.
32 *
33 * @file nat/nat_api_stun.c
34 * @brief Functions for STUN functionality
35 * @author Bruno Souza Cabral
36 */
37
38#include "platform.h"
39#include "gnunet_util_lib.h"
40#include "gnunet_resolver_service.h"
41#include "gnunet_nat_service.h"
42
43
44#include "nat_stun.h"
45
46#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__)
47
48#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
49
50
51/**
52 * Handle to a request given to the resolver. Can be used to cancel
53 * the request prior to the timeout or successful execution. Also
54 * used to track our internal state for the request.
55 */
56struct GNUNET_NAT_STUN_Handle
57{
58 /**
59 * Handle to a pending DNS lookup request.
60 */
61 struct GNUNET_RESOLVER_RequestHandle *dns_active;
62
63 /**
64 * Handle to the listen socket
65 */
66 struct GNUNET_NETWORK_Handle *sock;
67
68 /**
69 * Stun server address
70 */
71 char *stun_server;
72
73 /**
74 * Function to call when a error occurs
75 */
76 GNUNET_NAT_TestCallback cb;
77
78 /**
79 * Closure for @e cb.
80 */
81 void *cb_cls;
82
83 /**
84 * Do we got a DNS resolution successfully?
85 */
86 int dns_success;
87
88 /**
89 * STUN port
90 */
91 uint16_t stun_port;
92};
93
94
95/**
96 * Encode a class and method to a compatible STUN format
97 *
98 * @param msg_class class to be converted
99 * @param method method to be converted
100 * @return message in a STUN compatible format
101 */
102static int
103encode_message (enum StunClasses msg_class,
104 enum StunMethods method)
105{
106 return ((msg_class & 1) << 4) | ((msg_class & 2) << 7)
107 | (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800)
108 << 2);
109}
110
111
112/**
113 * Fill the stun_header with a random request_id
114 *
115 * @param req, stun header to be filled
116 */
117static void
118generate_request_id (struct stun_header *req)
119{
120 req->magic = htonl (STUN_MAGIC_COOKIE);
121 for (unsigned int x = 0; x < 3; x++)
122 req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
123 UINT32_MAX);
124}
125
126
127/**
128 * Try to establish a connection given the specified address.
129 *
130 * @param cls our `struct GNUNET_NAT_STUN_Handle *`
131 * @param addr address to try, NULL for "last call"
132 * @param addrlen length of @a addr
133 */
134static void
135stun_dns_callback (void *cls,
136 const struct sockaddr *addr,
137 socklen_t addrlen)
138{
139 struct GNUNET_NAT_STUN_Handle *rh = cls;
140 struct stun_header req;
141 struct sockaddr_in server;
142
143 if (NULL == addr)
144 {
145 rh->dns_active = NULL;
146 if (GNUNET_NO == rh->dns_success)
147 {
148 LOG (GNUNET_ERROR_TYPE_INFO,
149 "Error resolving host %s\n",
150 rh->stun_server);
151 rh->cb (rh->cb_cls,
152 GNUNET_NAT_ERROR_NOT_ONLINE);
153 }
154 else if (GNUNET_SYSERR == rh->dns_success)
155 {
156 rh->cb (rh->cb_cls,
157 GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR);
158 }
159 else
160 {
161 rh->cb (rh->cb_cls,
162 GNUNET_NAT_ERROR_SUCCESS);
163 }
164 GNUNET_NAT_stun_make_request_cancel (rh);
165 return;
166 }
167
168 rh->dns_success = GNUNET_YES;
169 memset (&server, 0, sizeof(server));
170 server.sin_family = AF_INET;
171 server.sin_addr = ((struct sockaddr_in *) addr)->sin_addr;
172 server.sin_port = htons (rh->stun_port);
173#if HAVE_SOCKADDR_IN_SIN_LEN
174 server.sin_len = (u_char) sizeof(struct sockaddr_in);
175#endif
176
177 /* Craft the simplest possible STUN packet. A request binding */
178 generate_request_id (&req);
179 req.msglen = htons (0);
180 req.msgtype = htons (encode_message (STUN_REQUEST,
181 STUN_BINDING));
182
183 /* Send the packet */
184 if (-1 ==
185 GNUNET_NETWORK_socket_sendto (rh->sock,
186 &req,
187 sizeof(req),
188 (const struct sockaddr *) &server,
189 sizeof(server)))
190 {
191 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
192 "sendto");
193 rh->dns_success = GNUNET_SYSERR;
194 return;
195 }
196}
197
198
199/**
200 * Make Generic STUN request. Sends a generic stun request to the
201 * server specified using the specified socket.
202 *
203 * @param server the address of the stun server
204 * @param port port of the stun server, in host byte order
205 * @param sock the socket used to send the request
206 * @param cb callback in case of error
207 * @param cb_cls closure for @a cb
208 * @return NULL on error
209 */
210struct GNUNET_NAT_STUN_Handle *
211GNUNET_NAT_stun_make_request (const char *server,
212 uint16_t port,
213 struct GNUNET_NETWORK_Handle *sock,
214 GNUNET_NAT_TestCallback cb,
215 void *cb_cls)
216{
217 struct GNUNET_NAT_STUN_Handle *rh;
218
219 rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle);
220 rh->sock = sock;
221 rh->cb = cb;
222 rh->cb_cls = cb_cls;
223 rh->stun_server = GNUNET_strdup (server);
224 rh->stun_port = port;
225 rh->dns_success = GNUNET_NO;
226 rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server,
227 AF_INET,
228 TIMEOUT,
229 &stun_dns_callback,
230 rh);
231 if (NULL == rh->dns_active)
232 {
233 GNUNET_NAT_stun_make_request_cancel (rh);
234 return NULL;
235 }
236 return rh;
237}
238
239
240/**
241 * Cancel active STUN request. Frees associated resources
242 * and ensures that the callback is no longer invoked.
243 *
244 * @param rh request to cancel
245 */
246void
247GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh)
248{
249 if (NULL != rh->dns_active)
250 {
251 GNUNET_RESOLVER_request_cancel (rh->dns_active);
252 rh->dns_active = NULL;
253 }
254 GNUNET_free (rh->stun_server);
255 GNUNET_free (rh);
256}
257
258
259/* end of nat_stun.c */
diff --git a/src/nat/nat_stun.h b/src/nat/nat_stun.h
deleted file mode 100644
index a31e0f8d6..000000000
--- a/src/nat/nat_stun.h
+++ /dev/null
@@ -1,267 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016 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 * Message types for STUN server resolution
22 *
23 * @file nat/nat_stun.h
24 * @brief Testcase for STUN library
25 * @author Bruno Souza Cabral
26 * @author Mark Spencer (Original code borrowed from Asterisk)
27 * @author Christian Grothoff
28 */
29
30
31#define STUN_IGNORE (0)
32#define STUN_ACCEPT (1)
33
34#define STUN_MAGIC_COOKIE 0x2112A442
35
36typedef struct
37{
38 uint32_t id[3];
39} GNUNET_PACKED stun_trans_id;
40
41
42struct stun_header
43{
44 uint16_t msgtype;
45 uint16_t msglen;
46 uint32_t magic;
47 stun_trans_id id;
48} GNUNET_PACKED;
49
50
51struct stun_attr
52{
53 uint16_t attr;
54 uint16_t len;
55} GNUNET_PACKED;
56
57
58/**
59 * The format normally used for addresses carried by STUN messages.
60 */
61struct stun_addr
62{
63 uint8_t unused;
64
65 /**
66 * Address family, we expect AF_INET.
67 */
68 uint8_t family;
69
70 /**
71 * Port number.
72 */
73 uint16_t port;
74
75 /**
76 * IPv4 address. Should this be "struct in_addr"?
77 */
78 uint32_t addr;
79} GNUNET_PACKED;
80
81
82/**
83 * STUN message classes
84 */
85enum StunClasses
86{
87 INVALID_CLASS = 0,
88 STUN_REQUEST = 0x0000,
89 STUN_INDICATION = 0x0001,
90 STUN_RESPONSE = 0x0002,
91 STUN_ERROR_RESPONSE = 0x0003
92};
93
94enum StunMethods
95{
96 INVALID_METHOD = 0,
97 STUN_BINDING = 0x0001,
98 STUN_SHARED_SECRET = 0x0002,
99 STUN_ALLOCATE = 0x0003,
100 STUN_REFRESH = 0x0004,
101 STUN_SEND = 0x0006,
102 STUN_DATA = 0x0007,
103 STUN_CREATE_PERMISSION = 0x0008,
104 STUN_CHANNEL_BIND = 0x0009
105};
106
107
108/**
109 * Basic attribute types in stun messages.
110 * Messages can also contain custom attributes (codes above 0x7fff)
111 */
112enum StunAttributes
113{
114 STUN_MAPPED_ADDRESS = 0x0001,
115 STUN_RESPONSE_ADDRESS = 0x0002,
116 STUN_CHANGE_ADDRESS = 0x0003,
117 STUN_SOURCE_ADDRESS = 0x0004,
118 STUN_CHANGED_ADDRESS = 0x0005,
119 STUN_USERNAME = 0x0006,
120 STUN_PASSWORD = 0x0007,
121 STUN_MESSAGE_INTEGRITY = 0x0008,
122 STUN_ERROR_CODE = 0x0009,
123 STUN_UNKNOWN_ATTRIBUTES = 0x000a,
124 STUN_REFLECTED_FROM = 0x000b,
125 STUN_REALM = 0x0014,
126 STUN_NONCE = 0x0015,
127 STUN_XOR_MAPPED_ADDRESS = 0x0020,
128 STUN_MS_VERSION = 0x8008,
129 STUN_MS_XOR_MAPPED_ADDRESS = 0x8020,
130 STUN_SOFTWARE = 0x8022,
131 STUN_ALTERNATE_SERVER = 0x8023,
132 STUN_FINGERPRINT = 0x8028
133};
134
135
136/**
137 * Convert a message to a StunClass
138 *
139 * @param msg the received message
140 * @return the converted StunClass
141 */
142static enum StunClasses
143decode_class (int msg)
144{
145 /* Sorry for the magic, but this maps the class according to rfc5245 */
146 return (enum StunClasses) ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7);
147}
148
149
150/**
151 * Convert a message to a StunMethod
152 *
153 * @param msg the received message
154 * @return the converted StunMethod
155 */
156static enum StunMethods
157decode_method (int msg)
158{
159 return (enum StunMethods) (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg
160 & 0x3e00)
161 >> 2);
162}
163
164
165/**
166 * Print a class and method from a STUN message
167 *
168 * @param msg
169 * @return string with the message class and method
170 */
171GNUNET_UNUSED
172static const char *
173stun_msg2str (int msg)
174{
175 static const struct
176 {
177 enum StunClasses value;
178 const char *name;
179 } classes[] = {
180 { STUN_REQUEST, "Request" },
181 { STUN_INDICATION, "Indication" },
182 { STUN_RESPONSE, "Response" },
183 { STUN_ERROR_RESPONSE, "Error Response" },
184 { INVALID_CLASS, NULL }
185 };
186 static const struct
187 {
188 enum StunMethods value;
189 const char *name;
190 } methods[] = {
191 { STUN_BINDING, "Binding" },
192 { INVALID_METHOD, NULL }
193 };
194 static char result[64];
195 const char *msg_class = NULL;
196 const char *method = NULL;
197 enum StunClasses cvalue;
198 enum StunMethods mvalue;
199
200 cvalue = decode_class (msg);
201 for (unsigned int i = 0; classes[i].name; i++)
202 if (classes[i].value == cvalue)
203 {
204 msg_class = classes[i].name;
205 break;
206 }
207 mvalue = decode_method (msg);
208 for (unsigned int i = 0; methods[i].name; i++)
209 if (methods[i].value == mvalue)
210 {
211 method = methods[i].name;
212 break;
213 }
214 GNUNET_snprintf (result,
215 sizeof(result),
216 "%s %s",
217 method ? : "Unknown Method",
218 msg_class ? : "Unknown Class Message");
219 return result;
220}
221
222
223/**
224 * Print attribute name
225 *
226 * @param msg with a attribute type
227 * @return string with the attribute name
228 */
229GNUNET_UNUSED
230static const char *
231stun_attr2str (enum StunAttributes msg)
232{
233 static const struct
234 {
235 enum StunAttributes value;
236 const char *name;
237 } attrs[] = {
238 { STUN_MAPPED_ADDRESS, "Mapped Address" },
239 { STUN_RESPONSE_ADDRESS, "Response Address" },
240 { STUN_CHANGE_ADDRESS, "Change Address" },
241 { STUN_SOURCE_ADDRESS, "Source Address" },
242 { STUN_CHANGED_ADDRESS, "Changed Address" },
243 { STUN_USERNAME, "Username" },
244 { STUN_PASSWORD, "Password" },
245 { STUN_MESSAGE_INTEGRITY, "Message Integrity" },
246 { STUN_ERROR_CODE, "Error Code" },
247 { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" },
248 { STUN_REFLECTED_FROM, "Reflected From" },
249 { STUN_REALM, "Realm" },
250 { STUN_NONCE, "Nonce" },
251 { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" },
252 { STUN_MS_VERSION, "MS Version" },
253 { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" },
254 { STUN_SOFTWARE, "Software" },
255 { STUN_ALTERNATE_SERVER, "Alternate Server" },
256 { STUN_FINGERPRINT, "Fingerprint" },
257 { 0, NULL }
258 };
259
260 for (unsigned int i = 0; attrs[i].name; i++)
261 if (attrs[i].value == msg)
262 return attrs[i].name;
263 return "Unknown Attribute";
264}
265
266
267/* end of nat_stun.h */
diff --git a/src/nat/test_nat.c b/src/nat/test_nat.c
deleted file mode 100644
index a3072f712..000000000
--- a/src/nat/test_nat.c
+++ /dev/null
@@ -1,192 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011 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 * Testcase for port redirection and public IP address retrieval.
23 * This test never fails, because there need to be a NAT box set up for that.
24 * So we only get IP address and open the 2086 port using any NAT traversal
25 * method available, wait for 30s, close ports and return.
26 * Have a look at the logs and use NMAP to check that it works with your box.
27 *
28 * @file nat/test_nat.c
29 * @brief Testcase for NAT library
30 * @author Milan Bouchet-Valat
31 * @author Christian Grothoff
32 *
33 * TODO: actually use ARM to start resolver service to make DNS work!
34 */
35
36#include "platform.h"
37#include "gnunet_util_lib.h"
38#include "gnunet_program_lib.h"
39#include "gnunet_scheduler_lib.h"
40#include "gnunet_nat_lib.h"
41
42
43/**
44 * Time to wait before stopping NAT, in seconds
45 */
46#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
47
48
49/**
50 * Function called on each address that the NAT service
51 * believes to be valid for the transport.
52 */
53static void
54addr_callback (void *cls, int add_remove, const struct sockaddr *addr,
55 socklen_t addrlen)
56{
57 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
58 "Address changed: %s `%s' (%u bytes)\n",
59 add_remove == GNUNET_YES ? "added" : "removed",
60 GNUNET_a2s (addr,
61 addrlen),
62 (unsigned int) addrlen);
63}
64
65
66/**
67 * Function that terminates the test.
68 */
69static void
70stop (void *cls)
71{
72 struct GNUNET_NAT_Handle *nat = cls;
73
74 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
75 "Stopping NAT and quitting...\n");
76 GNUNET_NAT_unregister (nat);
77}
78
79
80struct addr_cls
81{
82 struct sockaddr *addr;
83 socklen_t addrlen;
84};
85
86
87/**
88 * Return the address of the default interface,
89 * or any interface with a valid address if the default is not valid
90 *
91 * @param cls the 'struct addr_cls'
92 * @param name name of the interface
93 * @param isDefault do we think this may be our default interface
94 * @param addr address of the interface
95 * @param addrlen number of bytes in @a addr
96 * @return #GNUNET_OK to continue iterating
97 */
98static int
99process_if (void *cls,
100 const char *name,
101 int isDefault,
102 const struct sockaddr *addr,
103 const struct sockaddr *broadcast_addr,
104 const struct sockaddr *netmask,
105 socklen_t addrlen)
106{
107 struct addr_cls *data = cls;
108
109 if (addr == NULL)
110 return GNUNET_OK;
111 GNUNET_free (data->addr);
112 data->addr = GNUNET_malloc (addrlen);
113 GNUNET_memcpy (data->addr, addr, addrlen);
114 data->addrlen = addrlen;
115 if (isDefault)
116 return GNUNET_SYSERR;
117 return GNUNET_OK;
118}
119
120
121/**
122 * Main function run with scheduler.
123 */
124static void
125run (void *cls,
126 char *const *args,
127 const char *cfgfile,
128 const struct GNUNET_CONFIGURATION_Handle *cfg)
129{
130 struct GNUNET_NAT_Handle *nat;
131 struct addr_cls data;
132 struct sockaddr *addr;
133
134 data.addr = NULL;
135 GNUNET_OS_network_interfaces_list (process_if, &data);
136 if (NULL == data.addr)
137 {
138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 "Could not find a valid interface address!\n");
140 exit (77); /* marks test as skipped */
141 }
142 addr = data.addr;
143 GNUNET_assert (addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
144 if (addr->sa_family == AF_INET)
145 ((struct sockaddr_in *) addr)->sin_port = htons (2086);
146 else
147 ((struct sockaddr_in6 *) addr)->sin6_port = htons (2086);
148
149 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
150 "Requesting NAT redirection from address %s...\n",
151 GNUNET_a2s (addr, data.addrlen));
152
153 nat = GNUNET_NAT_register (cfg, GNUNET_YES /* tcp */,
154 2086, 1, (const struct sockaddr **) &addr,
155 &data.addrlen, &addr_callback, NULL, NULL, NULL);
156 GNUNET_free (addr);
157 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, nat);
158}
159
160
161int
162main (int argc, char *const argv[])
163{
164 struct GNUNET_GETOPT_CommandLineOption options[] = {
165 GNUNET_GETOPT_OPTION_END
166 };
167
168 char *const argv_prog[] = {
169 "test-nat",
170 "-c",
171 "test_nat_data.conf",
172 NULL
173 };
174
175 GNUNET_log_setup ("test-nat",
176 "WARNING",
177 NULL);
178 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
179 "Testing NAT library, timeout set to %s\n",
180 GNUNET_STRINGS_relative_time_to_string (TIMEOUT,
181 GNUNET_YES));
182 GNUNET_PROGRAM_run (3,
183 argv_prog,
184 "test-nat",
185 "nohelp",
186 options,
187 &run, NULL);
188 return 0;
189}
190
191
192/* end of test_nat.c */
diff --git a/src/nat/test_nat_data.conf b/src/nat/test_nat_data.conf
deleted file mode 100644
index e139d0c05..000000000
--- a/src/nat/test_nat_data.conf
+++ /dev/null
@@ -1,36 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/nat-test
3# GNUNET_TEST_HOME = /var/lib/gnunet/
4# configuration file is assumed to be the default,
5# which is what we want by default...
6
7[nat]
8# Are we behind NAT?
9BEHIND_NAT = YES
10
11# Is the NAT hole-punched?
12PUNCHED_NAT = NO
13
14# Disable UPNP by default until it gets cleaner!
15ENABLE_UPNP = YES
16
17# Use addresses from the local network interfaces (including loopback, but also others)
18USE_LOCALADDR = YES
19
20# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!)
21# normal interface IP address for non-NATed peers;
22# possibly auto-detected (using UPnP) if possible if not specified
23# EXTERNAL_ADDRESS =
24
25# Should we use ICMP-based NAT traversal to try connect to NATed peers
26# or, if we are behind NAT, to allow connections to us?
27ENABLE_ICMP_CLIENT = NO
28ENABLE_ICMP_SERVER = NO
29
30# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY;
31# normal interface IP address for non-NATed peers;
32# likely auto-detected (via interface list) if not specified (!)
33# INTERNAL_ADDRESS =
34
35# Disable IPv6 support
36DISABLEV6 = NO
diff --git a/src/nat/test_nat_mini.c b/src/nat/test_nat_mini.c
deleted file mode 100644
index 528815e1a..000000000
--- a/src/nat/test_nat_mini.c
+++ /dev/null
@@ -1,134 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011 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 * @file nat/test_nat_mini.c
22 * @brief Testcase for NAT library - mini
23 * @author Christian Grothoff
24 *
25 * Testcase for port redirection and public IP address retrieval.
26 * This test never fails, because there need to be a NAT box set up for that
27 *
28 * TODO: actually use ARM to start resolver service to make DNS work!
29 */
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_program_lib.h"
34#include "gnunet_scheduler_lib.h"
35#include "gnunet_nat_lib.h"
36
37/* Time to wait before stopping NAT, in seconds */
38#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
39
40/**
41 * Function called on each address that the NAT service
42 * believes to be valid for the transport.
43 */
44static void
45addr_callback (void *cls, int add_remove,
46 const struct sockaddr *addr,
47 socklen_t addrlen,
48 enum GNUNET_NAT_StatusCode ret)
49{
50 if (GNUNET_NAT_ERROR_SUCCESS == ret)
51 {
52 fprintf (stderr,
53 "Address changed: %s `%s' (%u bytes)\n",
54 add_remove == GNUNET_YES
55 ? "added" : "removed",
56 GNUNET_a2s (addr,
57 addrlen),
58 (unsigned int) addrlen);
59 }
60 else
61 ;
62 // TODO: proper error handling!
63}
64
65
66/**
67 * Function that terminates the test.
68 */
69static void
70stop (void *cls)
71{
72 struct GNUNET_NAT_MiniHandle *mini = cls;
73
74 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping NAT and quitting...\n");
75 GNUNET_NAT_mini_map_stop (mini);
76}
77
78
79#define PORT 10000
80
81/**
82 * Main function run with scheduler.
83 */
84static void
85run (void *cls, char *const *args, const char *cfgfile,
86 const struct GNUNET_CONFIGURATION_Handle *cfg)
87{
88 struct GNUNET_NAT_MiniHandle *mini;
89
90 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
91 "Requesting NAT redirection for port %u...\n",
92 PORT);
93 mini = GNUNET_NAT_mini_map_start (PORT, GNUNET_YES /* tcp */,
94 &addr_callback, NULL);
95 if (NULL == mini)
96 {
97 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Could not start UPnP interaction\n");
98 return;
99 }
100 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, mini);
101}
102
103
104int
105main (int argc, char *const argv[])
106{
107 struct GNUNET_GETOPT_CommandLineOption options[] = {
108 GNUNET_GETOPT_OPTION_END
109 };
110
111 char *const argv_prog[] = {
112 "test-nat-mini",
113 "-c",
114 "test_nat_data.conf",
115 "-L",
116 "WARNING",
117 NULL
118 };
119
120 GNUNET_log_setup ("test-nat-mini",
121 "WARNING",
122 NULL);
123
124 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
125 "UPnP test for NAT library, timeout set to %s\n",
126 GNUNET_STRINGS_relative_time_to_string (TIMEOUT,
127 GNUNET_YES));
128 GNUNET_PROGRAM_run (5, argv_prog, "test-nat-mini", "nohelp", options, &run,
129 NULL);
130 return 0;
131}
132
133
134/* end of test_nat_mini.c */
diff --git a/src/nat/test_nat_test.c b/src/nat/test_nat_test.c
deleted file mode 100644
index 2abab4d5f..000000000
--- a/src/nat/test_nat_test.c
+++ /dev/null
@@ -1,142 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2011, 2014 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 * @file nat/test_nat_test.c
22 * @brief Testcase for NAT testing functions
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_nat_lib.h"
28
29/**
30 * Time to wait before stopping NAT test, in seconds
31 */
32#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
33
34
35static int ret = 1;
36
37static struct GNUNET_NAT_Test *tst;
38
39static struct GNUNET_SCHEDULER_Task *tsk;
40
41
42static void
43report_result (void *cls,
44 enum GNUNET_NAT_StatusCode aret)
45{
46 if (GNUNET_NAT_ERROR_TIMEOUT == aret)
47 fprintf (stderr,
48 "NAT test timed out\n");
49 else if (GNUNET_NAT_ERROR_SUCCESS != aret)
50 fprintf (stderr,
51 "NAT test reported error %d\n", aret);
52 else
53 ret = 0;
54 GNUNET_NAT_test_stop (tst);
55 tst = NULL;
56 GNUNET_SCHEDULER_cancel (tsk);
57 tsk = NULL;
58}
59
60
61static void
62failed_timeout (void *cls)
63{
64 tsk = NULL;
65 fprintf (stderr,
66 "NAT test failed to terminate on timeout\n");
67 ret = 2;
68 GNUNET_NAT_test_stop (tst);
69 tst = NULL;
70}
71
72
73/**
74 * Main function run with scheduler.
75 */
76static void
77run (void *cls, char *const *args, const char *cfgfile,
78 const struct GNUNET_CONFIGURATION_Handle *cfg)
79{
80 tst =
81 GNUNET_NAT_test_start (cfg, GNUNET_YES, 1285, 1285, TIMEOUT,
82 &report_result,
83 NULL);
84 tsk = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (TIMEOUT,
85 2),
86 &failed_timeout,
87 NULL);
88}
89
90
91int
92main (int argc, char *const argv[])
93{
94 struct GNUNET_GETOPT_CommandLineOption options[] = {
95 GNUNET_GETOPT_OPTION_END
96 };
97 struct GNUNET_OS_Process *gns;
98 int nat_res;
99 char *const argv_prog[] = {
100 "test-nat-test",
101 "-c",
102 "test_nat_test_data.conf",
103 NULL
104 };
105
106 GNUNET_log_setup ("test-nat-test",
107 "WARNING",
108 NULL);
109
110 nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server", GNUNET_NO,
111 NULL);
112 if (GNUNET_SYSERR == nat_res)
113 {
114 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
115 "Cannot run NAT test: `%s' file not found\n",
116 "gnunet-nat-server");
117 return 0;
118 }
119
120 gns = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
121 | GNUNET_OS_USE_PIPE_CONTROL,
122 NULL, NULL, NULL,
123 "gnunet-nat-server",
124 "gnunet-nat-server",
125 "-c", "test_nat_test_data.conf",
126 "12345", NULL);
127 GNUNET_assert (NULL != gns);
128 GNUNET_PROGRAM_run (3, argv_prog,
129 "test-nat-test", "nohelp",
130 options, &run,
131 NULL);
132 GNUNET_break (0 == GNUNET_OS_process_kill (gns, GNUNET_TERM_SIG));
133 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (gns));
134 GNUNET_OS_process_destroy (gns);
135 if (0 != ret)
136 fprintf (stderr,
137 "NAT test failed to report success\n");
138 return ret;
139}
140
141
142/* end of test_nat_test.c */
diff --git a/src/nat/test_nat_test_data.conf b/src/nat/test_nat_test_data.conf
deleted file mode 100644
index ca78ca9f3..000000000
--- a/src/nat/test_nat_test_data.conf
+++ /dev/null
@@ -1,45 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/nat-test
3# GNUNET_TEST_HOME = /var/lib/gnunet/
4# configuration file is assumed to be the default,
5# which is what we want by default...
6
7[gnunet-nat-server]
8HOSTNAME = localhost
9PORT = 57315
10
11[nat]
12# Are we behind NAT?
13BEHIND_NAT = NO
14
15# Is the NAT hole-punched?
16PUNCHED_NAT = YES
17
18# Disable UPNP by default until it gets cleaner!
19ENABLE_UPNP = NO
20
21# Use addresses from the local network interfaces (including loopback, but also others)
22USE_LOCALADDR = YES
23RETURN_LOCAL_ADDRESSES = YES
24
25# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!)
26# normal interface IP address for non-NATed peers;
27# possibly auto-detected (using UPnP) if possible if not specified
28# EXTERNAL_ADDRESS =
29
30# Should we use ICMP-based NAT traversal to try connect to NATed peers
31# or, if we are behind NAT, to allow connections to us?
32ENABLE_ICMP_CLIENT = NO
33ENABLE_ICMP_SERVER = NO
34
35# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY;
36# normal interface IP address for non-NATed peers;
37# likely auto-detected (via interface list) if not specified (!)
38INTERNAL_ADDRESS = 127.0.0.1
39
40# Disable IPv6 support
41DISABLEV6 = YES
42
43
44[nse]
45START_ON_DEMAND = NO
diff --git a/src/nat/test_stun.c b/src/nat/test_stun.c
deleted file mode 100644
index 75eb877b3..000000000
--- a/src/nat/test_stun.c
+++ /dev/null
@@ -1,313 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015 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 * Testcase for STUN server resolution
23 *
24 * @file nat/test_stun.c
25 * @brief Testcase for STUN library
26 * @author Bruno Souza Cabral
27 * @author Christian Grothoff
28 */
29
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_program_lib.h"
34#include "gnunet_scheduler_lib.h"
35#include "gnunet_nat_lib.h"
36
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "test-stun", __VA_ARGS__)
39
40/**
41 * Time to wait before stopping NAT, in seconds
42 */
43#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
44
45
46/**
47 * The port the test service is running on (default 7895)
48 */
49static unsigned long port = 7895;
50
51static int ret = 1;
52
53static const char *stun_server = "stun.gnunet.org";
54
55static int stun_port = 3478;
56
57/**
58 * The listen socket of the service for IPv4
59 */
60static struct GNUNET_NETWORK_Handle *lsock4;
61
62/**
63 * The listen task ID for IPv4
64 */
65static struct GNUNET_SCHEDULER_Task *ltask4;
66
67/**
68 * Handle for the STUN request.
69 */
70static struct GNUNET_NAT_STUN_Handle *rh;
71
72
73static void
74print_answer (struct sockaddr_in*answer)
75{
76 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
77 "External IP is: %s , with port %d\n",
78 inet_ntoa (answer->sin_addr),
79 ntohs (answer->sin_port));
80}
81
82
83/**
84 * Function that terminates the test.
85 */
86static void
87stop ()
88{
89 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
90 "Stopping NAT and quitting...\n");
91 if (NULL != ltask4)
92 {
93 GNUNET_SCHEDULER_cancel (ltask4);
94 ltask4 = NULL;
95 }
96 if (NULL != lsock4)
97 {
98 GNUNET_NETWORK_socket_close (lsock4);
99 lsock4 = NULL;
100 }
101 if (NULL != rh)
102 {
103 GNUNET_NAT_stun_make_request_cancel (rh);
104 rh = NULL;
105 }
106}
107
108
109/**
110 * Activity on our incoming socket. Read data from the
111 * incoming connection.
112 *
113 * @param cls
114 */
115static void
116do_udp_read (void *cls)
117{
118 // struct GNUNET_NAT_Test *tst = cls;
119 unsigned char reply_buf[1024];
120 ssize_t rlen;
121 struct sockaddr_in answer;
122 const struct GNUNET_SCHEDULER_TaskContext *tc;
123
124 ltask4 = NULL;
125 tc = GNUNET_SCHEDULER_get_task_context ();
126 if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) ||
127 (! GNUNET_NETWORK_fdset_isset (tc->read_ready,
128 lsock4)))
129 {
130 fprintf (stderr,
131 "Timeout waiting for STUN response\n");
132 stop ();
133 }
134 rlen = GNUNET_NETWORK_socket_recv (lsock4,
135 reply_buf,
136 sizeof(reply_buf));
137 memset (&answer,
138 0,
139 sizeof(struct sockaddr_in));
140 if (GNUNET_OK !=
141 GNUNET_NAT_stun_handle_packet (reply_buf,
142 rlen,
143 &answer))
144 {
145 fprintf (stderr,
146 "Unexpected UDP packet, trying to read more\n");
147 ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT,
148 lsock4,
149 &do_udp_read, NULL);
150 return;
151 }
152 ret = 0;
153 print_answer (&answer);
154 stop ();
155}
156
157
158/**
159 * Create an IPv4 listen socket bound to our port.
160 *
161 * @return NULL on error
162 */
163static struct GNUNET_NETWORK_Handle *
164bind_v4 ()
165{
166 struct GNUNET_NETWORK_Handle *ls;
167 struct sockaddr_in sa4;
168 int eno;
169
170 memset (&sa4, 0, sizeof(sa4));
171 sa4.sin_family = AF_INET;
172 sa4.sin_port = htons (port);
173#if HAVE_SOCKADDR_IN_SIN_LEN
174 sa4.sin_len = sizeof(sa4);
175#endif
176 ls = GNUNET_NETWORK_socket_create (AF_INET,
177 SOCK_DGRAM,
178 0);
179 if (NULL == ls)
180 return NULL;
181 if (GNUNET_OK !=
182 GNUNET_NETWORK_socket_bind (ls,
183 (const struct sockaddr *) &sa4,
184 sizeof(sa4)))
185 {
186 eno = errno;
187 GNUNET_NETWORK_socket_close (ls);
188 errno = eno;
189 return NULL;
190 }
191 return ls;
192}
193
194
195/**
196 * Function called with the result of the STUN request transmission attempt.
197 *
198 * @param cls unused
199 * @param error status code from STUN
200 */
201static void
202request_callback (void *cls,
203 enum GNUNET_NAT_StatusCode error)
204{
205 rh = NULL;
206 if (GNUNET_NAT_ERROR_SUCCESS == error)
207 {
208 /* all good, start to receive */
209 ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT,
210 lsock4,
211 &do_udp_read,
212 NULL);
213 return;
214 }
215 if (error == GNUNET_NAT_ERROR_NOT_ONLINE)
216 {
217 ret = 77; /* report 'skip' */
218 fprintf (stderr,
219 "System is offline, cannot test STUN request.\n");
220 }
221 else
222 {
223 ret = error;
224 }
225 stop ();
226}
227
228
229/**
230 * Main function run with scheduler.
231 */
232static void
233run (void *cls,
234 char *const *args,
235 const char *cfgfile,
236 const struct GNUNET_CONFIGURATION_Handle *cfg)
237{
238 // Lets create the socket
239 lsock4 = bind_v4 ();
240 if (NULL == lsock4)
241 {
242 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
243 "bind");
244 GNUNET_SCHEDULER_shutdown ();
245 return;
246 }
247 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248 "Service listens on port %u\n",
249 (unsigned int) port);
250 rh = GNUNET_NAT_stun_make_request (stun_server,
251 stun_port,
252 lsock4,
253 &request_callback, NULL);
254 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
255 &stop, NULL);
256}
257
258
259int
260main (int argc, char *const argv[])
261{
262 struct GNUNET_GETOPT_CommandLineOption options[] = {
263 GNUNET_GETOPT_OPTION_END
264 };
265 char *const argv_prog[] = {
266 "test-stun",
267 "-c",
268 "test_stun.conf",
269 NULL
270 };
271 char *fn;
272 struct GNUNET_OS_Process *proc;
273
274 GNUNET_log_setup ("test-stun",
275 "WARNING",
276 NULL);
277
278 /* Lets start resolver */
279 fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
280 proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
281 | GNUNET_OS_USE_PIPE_CONTROL,
282 NULL, NULL, NULL,
283 fn,
284 "gnunet-service-resolver",
285 "-c", "test_stun.conf", NULL);
286
287 if (NULL == proc)
288 {
289 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
290 "This test was unable to start gnunet-service-resolver, and it is required to run ...\n");
291 exit (1);
292 }
293
294 GNUNET_PROGRAM_run (3, argv_prog,
295 "test-stun", "nohelp",
296 options,
297 &run, NULL);
298
299 /* Now kill the resolver */
300 if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
301 {
302 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
303 }
304 GNUNET_OS_process_wait (proc);
305 GNUNET_OS_process_destroy (proc);
306 proc = NULL;
307 GNUNET_free (fn);
308
309 return ret;
310}
311
312
313/* end of test_stun.c */
diff --git a/src/nat/test_stun.conf b/src/nat/test_stun.conf
deleted file mode 100644
index b03bbfff3..000000000
--- a/src/nat/test_stun.conf
+++ /dev/null
@@ -1,7 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-stun
3
4[resolver]
5PORT = 22354
6HOSTNAME = localhost
7