diff options
Diffstat (limited to 'src/service/nat-auto')
-rw-r--r-- | src/service/nat-auto/.gitignore | 2 | ||||
-rw-r--r-- | src/service/nat-auto/Makefile.am | 40 | ||||
-rw-r--r-- | src/service/nat-auto/gnunet-nat-auto_legacy.c | 597 | ||||
-rw-r--r-- | src/service/nat-auto/gnunet-service-nat-auto.c | 488 | ||||
-rw-r--r-- | src/service/nat-auto/gnunet-service-nat-auto_legacy.c | 1093 | ||||
-rw-r--r-- | src/service/nat-auto/meson.build | 36 | ||||
-rw-r--r-- | src/service/nat-auto/nat-auto.conf.in | 15 | ||||
-rw-r--r-- | src/service/nat-auto/nat-auto.h | 107 | ||||
-rw-r--r-- | src/service/nat-auto/nat_auto_api.c | 292 | ||||
-rw-r--r-- | src/service/nat-auto/nat_auto_api_test.c | 602 |
10 files changed, 3272 insertions, 0 deletions
diff --git a/src/service/nat-auto/.gitignore b/src/service/nat-auto/.gitignore new file mode 100644 index 000000000..9bb5406ed --- /dev/null +++ b/src/service/nat-auto/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-service-nat-auto | ||
2 | |||
diff --git a/src/service/nat-auto/Makefile.am b/src/service/nat-auto/Makefile.am new file mode 100644 index 000000000..d34a16761 --- /dev/null +++ b/src/service/nat-auto/Makefile.am | |||
@@ -0,0 +1,40 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | libexecdir= $(pkglibdir)/libexec/ | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | pkgcfg_DATA = \ | ||
9 | nat-auto.conf | ||
10 | |||
11 | libexec_PROGRAMS = \ | ||
12 | gnunet-service-nat-auto | ||
13 | |||
14 | if USE_COVERAGE | ||
15 | AM_CFLAGS = -fprofile-arcs -ftest-coverage | ||
16 | endif | ||
17 | |||
18 | lib_LTLIBRARIES = \ | ||
19 | libgnunetnatauto.la | ||
20 | |||
21 | libgnunetnatauto_la_SOURCES = \ | ||
22 | nat_auto_api.c \ | ||
23 | nat_auto_api_test.c | ||
24 | libgnunetnatauto_la_LIBADD = \ | ||
25 | $(top_builddir)/src/service/nat/libgnunetnatnew.la \ | ||
26 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
27 | $(GN_LIBINTL) @EXT_LIBS@ | ||
28 | libgnunetnatauto_la_LDFLAGS = \ | ||
29 | $(GN_LIB_LDFLAGS) \ | ||
30 | -version-info 0:0:0 | ||
31 | |||
32 | gnunet_service_nat_auto_SOURCES = \ | ||
33 | gnunet-service-nat-auto.c nat-auto.h | ||
34 | gnunet_service_nat_auto_LDADD = \ | ||
35 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
36 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
37 | $(top_builddir)/src/service/nat/libgnunetnatnew.la \ | ||
38 | $(LIBGCRYPT_LIBS) \ | ||
39 | -lgcrypt \ | ||
40 | $(GN_LIBINTL) | ||
diff --git a/src/service/nat-auto/gnunet-nat-auto_legacy.c b/src/service/nat-auto/gnunet-nat-auto_legacy.c new file mode 100644 index 000000000..4334cc953 --- /dev/null +++ b/src/service/nat-auto/gnunet-nat-auto_legacy.c | |||
@@ -0,0 +1,597 @@ | |||
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 nat/nat_test.c | ||
23 | * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_nat_lib.h" | ||
29 | #include "nat.h" | ||
30 | |||
31 | #define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__) | ||
32 | |||
33 | #define NAT_SERVER_TIMEOUT \ | ||
34 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | ||
35 | |||
36 | /** | ||
37 | * Entry we keep for each incoming connection. | ||
38 | */ | ||
39 | struct NatActivity | ||
40 | { | ||
41 | /** | ||
42 | * This is a doubly-linked list. | ||
43 | */ | ||
44 | struct NatActivity *next; | ||
45 | |||
46 | /** | ||
47 | * This is a doubly-linked list. | ||
48 | */ | ||
49 | struct NatActivity *prev; | ||
50 | |||
51 | /** | ||
52 | * Socket of the incoming connection. | ||
53 | */ | ||
54 | struct GNUNET_NETWORK_Handle *sock; | ||
55 | |||
56 | /** | ||
57 | * Handle of the master context. | ||
58 | */ | ||
59 | struct GNUNET_NAT_Test *h; | ||
60 | |||
61 | /** | ||
62 | * Task reading from the incoming connection. | ||
63 | */ | ||
64 | struct GNUNET_SCHEDULER_Task *rtask; | ||
65 | }; | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Entry we keep for each connection to the gnunet-nat-service. | ||
70 | */ | ||
71 | struct ClientActivity | ||
72 | { | ||
73 | /** | ||
74 | * This is a doubly-linked list. | ||
75 | */ | ||
76 | struct ClientActivity *next; | ||
77 | |||
78 | /** | ||
79 | * This is a doubly-linked list. | ||
80 | */ | ||
81 | struct ClientActivity *prev; | ||
82 | |||
83 | /** | ||
84 | * Socket of the incoming connection. | ||
85 | */ | ||
86 | struct GNUNET_MQ_Handle *mq; | ||
87 | |||
88 | /** | ||
89 | * Handle to overall NAT test. | ||
90 | */ | ||
91 | struct GNUNET_NAT_Test *h; | ||
92 | }; | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Handle to a NAT test. | ||
97 | */ | ||
98 | struct GNUNET_NAT_Test | ||
99 | { | ||
100 | /** | ||
101 | * Configuration used | ||
102 | */ | ||
103 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
104 | |||
105 | /** | ||
106 | * Function to call with success report | ||
107 | */ | ||
108 | GNUNET_NAT_TestCallback report; | ||
109 | |||
110 | /** | ||
111 | * Closure for @e report. | ||
112 | */ | ||
113 | void *report_cls; | ||
114 | |||
115 | /** | ||
116 | * Handle to NAT traversal in use | ||
117 | */ | ||
118 | struct GNUNET_NAT_Handle *nat; | ||
119 | |||
120 | /** | ||
121 | * Handle to listen socket, or NULL | ||
122 | */ | ||
123 | struct GNUNET_NETWORK_Handle *lsock; | ||
124 | |||
125 | /** | ||
126 | * Head of list of nat activities. | ||
127 | */ | ||
128 | struct NatActivity *na_head; | ||
129 | |||
130 | /** | ||
131 | * Tail of list of nat activities. | ||
132 | */ | ||
133 | struct NatActivity *na_tail; | ||
134 | |||
135 | /** | ||
136 | * Head of list of client activities. | ||
137 | */ | ||
138 | struct ClientActivity *ca_head; | ||
139 | |||
140 | /** | ||
141 | * Tail of list of client activities. | ||
142 | */ | ||
143 | struct ClientActivity *ca_tail; | ||
144 | |||
145 | /** | ||
146 | * Identity of task for the listen socket (if any) | ||
147 | */ | ||
148 | struct GNUNET_SCHEDULER_Task *ltask; | ||
149 | |||
150 | /** | ||
151 | * Task identifier for the timeout (if any) | ||
152 | */ | ||
153 | struct GNUNET_SCHEDULER_Task *ttask; | ||
154 | |||
155 | /** | ||
156 | * #GNUNET_YES if we're testing TCP | ||
157 | */ | ||
158 | int is_tcp; | ||
159 | |||
160 | /** | ||
161 | * Data that should be transmitted or source-port. | ||
162 | */ | ||
163 | uint16_t data; | ||
164 | |||
165 | /** | ||
166 | * Advertised port to the other peer. | ||
167 | */ | ||
168 | uint16_t adv_port; | ||
169 | |||
170 | /** | ||
171 | * Status code to be reported to the timeout/status call | ||
172 | */ | ||
173 | enum GNUNET_NAT_StatusCode status; | ||
174 | }; | ||
175 | |||
176 | |||
177 | /** | ||
178 | * Function called from #GNUNET_NAT_register whenever someone asks us | ||
179 | * to do connection reversal. | ||
180 | * | ||
181 | * @param cls closure, our `struct GNUNET_NAT_Handle` | ||
182 | * @param addr public IP address of the other peer | ||
183 | * @param addrlen actual length of the @a addr | ||
184 | */ | ||
185 | static void | ||
186 | reversal_cb (void *cls, const struct sockaddr *addr, socklen_t addrlen) | ||
187 | { | ||
188 | struct GNUNET_NAT_Test *h = cls; | ||
189 | const struct sockaddr_in *sa; | ||
190 | |||
191 | if (sizeof(struct sockaddr_in) != addrlen) | ||
192 | return; | ||
193 | sa = (const struct sockaddr_in *) addr; | ||
194 | if (h->data != sa->sin_port) | ||
195 | { | ||
196 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
197 | "Received connection reversal request for wrong port\n"); | ||
198 | return; /* wrong port */ | ||
199 | } | ||
200 | /* report success */ | ||
201 | h->report (h->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
202 | } | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Activity on our incoming socket. Read data from the | ||
207 | * incoming connection. | ||
208 | * | ||
209 | * @param cls the `struct GNUNET_NAT_Test` | ||
210 | */ | ||
211 | static void | ||
212 | do_udp_read (void *cls) | ||
213 | { | ||
214 | struct GNUNET_NAT_Test *tst = cls; | ||
215 | uint16_t data; | ||
216 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
217 | |||
218 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
219 | tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
220 | tst->lsock, | ||
221 | &do_udp_read, | ||
222 | tst); | ||
223 | if ((NULL != tc->write_ready) && | ||
224 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, tst->lsock)) && | ||
225 | (sizeof(data) == | ||
226 | GNUNET_NETWORK_socket_recv (tst->lsock, &data, sizeof(data)))) | ||
227 | { | ||
228 | if (data == tst->data) | ||
229 | tst->report (tst->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
230 | else | ||
231 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
232 | "Received data mismatches expected value\n"); | ||
233 | } | ||
234 | else | ||
235 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
236 | "Failed to receive data from inbound connection\n"); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Activity on our incoming socket. Read data from the | ||
242 | * incoming connection. | ||
243 | * | ||
244 | * @param cls the `struct NatActivity` | ||
245 | */ | ||
246 | static void | ||
247 | do_read (void *cls) | ||
248 | { | ||
249 | struct NatActivity *na = cls; | ||
250 | struct GNUNET_NAT_Test *tst; | ||
251 | uint16_t data; | ||
252 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
253 | |||
254 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
255 | na->rtask = NULL; | ||
256 | tst = na->h; | ||
257 | GNUNET_CONTAINER_DLL_remove (tst->na_head, tst->na_tail, na); | ||
258 | if ((NULL != tc->write_ready) && | ||
259 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, na->sock)) && | ||
260 | (sizeof(data) == | ||
261 | GNUNET_NETWORK_socket_recv (na->sock, &data, sizeof(data)))) | ||
262 | { | ||
263 | if (data == tst->data) | ||
264 | tst->report (tst->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
265 | else | ||
266 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
267 | "Received data does not match expected value\n"); | ||
268 | } | ||
269 | else | ||
270 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
271 | "Failed to receive data from inbound connection\n"); | ||
272 | GNUNET_NETWORK_socket_close (na->sock); | ||
273 | GNUNET_free (na); | ||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Activity on our listen socket. Accept the | ||
279 | * incoming connection. | ||
280 | * | ||
281 | * @param cls the `struct GNUNET_NAT_Test` | ||
282 | */ | ||
283 | static void | ||
284 | do_accept (void *cls) | ||
285 | { | ||
286 | struct GNUNET_NAT_Test *tst = cls; | ||
287 | struct GNUNET_NETWORK_Handle *s; | ||
288 | struct NatActivity *wl; | ||
289 | |||
290 | tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
291 | tst->lsock, | ||
292 | &do_accept, | ||
293 | tst); | ||
294 | s = GNUNET_NETWORK_socket_accept (tst->lsock, NULL, NULL); | ||
295 | if (NULL == s) | ||
296 | { | ||
297 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept"); | ||
298 | return; /* odd error */ | ||
299 | } | ||
300 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
301 | "Got an inbound connection, waiting for data\n"); | ||
302 | wl = GNUNET_new (struct NatActivity); | ||
303 | wl->sock = s; | ||
304 | wl->h = tst; | ||
305 | wl->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
306 | wl->sock, | ||
307 | &do_read, | ||
308 | wl); | ||
309 | GNUNET_CONTAINER_DLL_insert (tst->na_head, tst->na_tail, wl); | ||
310 | } | ||
311 | |||
312 | |||
313 | /** | ||
314 | * We got disconnected from the NAT server. Stop | ||
315 | * waiting for a reply. | ||
316 | * | ||
317 | * @param cls the `struct ClientActivity` | ||
318 | * @param error error code | ||
319 | */ | ||
320 | static void | ||
321 | mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
322 | { | ||
323 | struct ClientActivity *ca = cls; | ||
324 | struct GNUNET_NAT_Test *tst = ca->h; | ||
325 | |||
326 | GNUNET_CONTAINER_DLL_remove (tst->ca_head, tst->ca_tail, ca); | ||
327 | GNUNET_MQ_destroy (ca->mq); | ||
328 | GNUNET_free (ca); | ||
329 | } | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Address-callback, used to send message to gnunet-nat-server. | ||
334 | * | ||
335 | * @param cls closure | ||
336 | * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean | ||
337 | * the previous (now invalid) one | ||
338 | * @param addr either the previous or the new public IP address | ||
339 | * @param addrlen actual length of the @a addr | ||
340 | */ | ||
341 | static void | ||
342 | addr_cb (void *cls, | ||
343 | int add_remove, | ||
344 | const struct sockaddr *addr, | ||
345 | socklen_t addrlen) | ||
346 | { | ||
347 | struct GNUNET_NAT_Test *h = cls; | ||
348 | struct ClientActivity *ca; | ||
349 | struct GNUNET_MQ_Envelope *env; | ||
350 | struct GNUNET_NAT_TestMessage *msg; | ||
351 | const struct sockaddr_in *sa; | ||
352 | |||
353 | if (GNUNET_YES != add_remove) | ||
354 | return; | ||
355 | if (addrlen != sizeof(struct sockaddr_in)) | ||
356 | { | ||
357 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
358 | "NAT test ignores IPv6 address `%s' returned from NAT library\n", | ||
359 | GNUNET_a2s (addr, addrlen)); | ||
360 | return; /* ignore IPv6 here */ | ||
361 | } | ||
362 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
363 | "Asking gnunet-nat-server to connect to `%s'\n", | ||
364 | GNUNET_a2s (addr, addrlen)); | ||
365 | |||
366 | ca = GNUNET_new (struct ClientActivity); | ||
367 | ca->h = h; | ||
368 | ca->mq = GNUNET_CLIENT_connect (h->cfg, | ||
369 | "gnunet-nat-server", | ||
370 | NULL, | ||
371 | &mq_error_handler, | ||
372 | ca); | ||
373 | if (NULL == ca->mq) | ||
374 | { | ||
375 | GNUNET_free (ca); | ||
376 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
377 | _ ("Failed to connect to `gnunet-nat-server'\n")); | ||
378 | return; | ||
379 | } | ||
380 | GNUNET_CONTAINER_DLL_insert (h->ca_head, h->ca_tail, ca); | ||
381 | sa = (const struct sockaddr_in *) addr; | ||
382 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAT_TEST); | ||
383 | msg->dst_ipv4 = sa->sin_addr.s_addr; | ||
384 | msg->dport = sa->sin_port; | ||
385 | msg->data = h->data; | ||
386 | msg->is_tcp = htonl ((uint32_t) h->is_tcp); | ||
387 | GNUNET_MQ_send (ca->mq, env); | ||
388 | } | ||
389 | |||
390 | |||
391 | /** | ||
392 | * Timeout task for a nat test. | ||
393 | * Calls the report-callback with a timeout return value | ||
394 | * | ||
395 | * Destroys the nat handle after the callback has been processed. | ||
396 | * | ||
397 | * @param cls handle to the timed out NAT test | ||
398 | */ | ||
399 | static void | ||
400 | do_timeout (void *cls) | ||
401 | { | ||
402 | struct GNUNET_NAT_Test *nh = cls; | ||
403 | |||
404 | nh->ttask = NULL; | ||
405 | nh->report (nh->report_cls, | ||
406 | (GNUNET_NAT_ERROR_SUCCESS == nh->status) | ||
407 | ? GNUNET_NAT_ERROR_TIMEOUT | ||
408 | : nh->status); | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | ||
413 | * Start testing if NAT traversal works using the | ||
414 | * given configuration (IPv4-only). | ||
415 | * | ||
416 | * ALL failures are reported directly to the report callback | ||
417 | * | ||
418 | * @param cfg configuration for the NAT traversal | ||
419 | * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP | ||
420 | * @param bnd_port port to bind to, 0 for connection reversal | ||
421 | * @param adv_port externally advertised port to use | ||
422 | * @param timeout delay after which the test should be aborted | ||
423 | * @param report function to call with the result of the test | ||
424 | * @param report_cls closure for @a report | ||
425 | * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback | ||
426 | */ | ||
427 | struct GNUNET_NAT_Test * | ||
428 | GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
429 | int is_tcp, | ||
430 | uint16_t bnd_port, | ||
431 | uint16_t adv_port, | ||
432 | struct GNUNET_TIME_Relative timeout, | ||
433 | GNUNET_NAT_TestCallback report, | ||
434 | void *report_cls) | ||
435 | { | ||
436 | struct GNUNET_NAT_Test *nh; | ||
437 | struct sockaddr_in sa; | ||
438 | const struct sockaddr *addrs[] = { (const struct sockaddr *) &sa }; | ||
439 | const socklen_t addrlens[] = { sizeof(sa) }; | ||
440 | |||
441 | memset (&sa, 0, sizeof(sa)); | ||
442 | sa.sin_family = AF_INET; | ||
443 | sa.sin_port = htons (bnd_port); | ||
444 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
445 | sa.sin_len = sizeof(sa); | ||
446 | #endif | ||
447 | |||
448 | nh = GNUNET_new (struct GNUNET_NAT_Test); | ||
449 | nh->cfg = cfg; | ||
450 | nh->is_tcp = is_tcp; | ||
451 | nh->data = bnd_port; | ||
452 | nh->adv_port = adv_port; | ||
453 | nh->report = report; | ||
454 | nh->report_cls = report_cls; | ||
455 | nh->status = GNUNET_NAT_ERROR_SUCCESS; | ||
456 | if (0 == bnd_port) | ||
457 | { | ||
458 | nh->nat = GNUNET_NAT_register (cfg, | ||
459 | is_tcp, | ||
460 | 0, | ||
461 | 0, | ||
462 | NULL, | ||
463 | NULL, | ||
464 | &addr_cb, | ||
465 | &reversal_cb, | ||
466 | nh, | ||
467 | NULL); | ||
468 | } | ||
469 | else | ||
470 | { | ||
471 | nh->lsock = | ||
472 | GNUNET_NETWORK_socket_create (AF_INET, | ||
473 | (is_tcp == GNUNET_YES) ? SOCK_STREAM | ||
474 | : SOCK_DGRAM, | ||
475 | 0); | ||
476 | if ((nh->lsock == NULL) || | ||
477 | (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh->lsock, | ||
478 | (const struct sockaddr *) &sa, | ||
479 | sizeof(sa)))) | ||
480 | { | ||
481 | GNUNET_log ( | ||
482 | GNUNET_ERROR_TYPE_ERROR, | ||
483 | _ ("Failed to create listen socket bound to `%s' for NAT test: %s\n"), | ||
484 | GNUNET_a2s ((const struct sockaddr *) &sa, sizeof(sa)), | ||
485 | strerror (errno)); | ||
486 | if (NULL != nh->lsock) | ||
487 | { | ||
488 | GNUNET_NETWORK_socket_close (nh->lsock); | ||
489 | nh->lsock = NULL; | ||
490 | } | ||
491 | nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR; | ||
492 | nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, nh); | ||
493 | return nh; | ||
494 | } | ||
495 | if (GNUNET_YES == is_tcp) | ||
496 | { | ||
497 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_listen (nh->lsock, 5)); | ||
498 | nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
499 | nh->lsock, | ||
500 | &do_accept, | ||
501 | nh); | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
506 | nh->lsock, | ||
507 | &do_udp_read, | ||
508 | nh); | ||
509 | } | ||
510 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
511 | "NAT test listens on port %u (%s)\n", | ||
512 | bnd_port, | ||
513 | (GNUNET_YES == is_tcp) ? "tcp" : "udp"); | ||
514 | nh->nat = GNUNET_NAT_register (cfg, | ||
515 | is_tcp, | ||
516 | adv_port, | ||
517 | 1, | ||
518 | addrs, | ||
519 | addrlens, | ||
520 | &addr_cb, | ||
521 | NULL, | ||
522 | nh, | ||
523 | NULL); | ||
524 | if (NULL == nh->nat) | ||
525 | { | ||
526 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
527 | _ ("NAT test failed to start NAT library\n")); | ||
528 | if (NULL != nh->ltask) | ||
529 | { | ||
530 | GNUNET_SCHEDULER_cancel (nh->ltask); | ||
531 | nh->ltask = NULL; | ||
532 | } | ||
533 | if (NULL != nh->lsock) | ||
534 | { | ||
535 | GNUNET_NETWORK_socket_close (nh->lsock); | ||
536 | nh->lsock = NULL; | ||
537 | } | ||
538 | nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED; | ||
539 | nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout, nh); | ||
540 | return nh; | ||
541 | } | ||
542 | } | ||
543 | nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout, &do_timeout, nh); | ||
544 | return nh; | ||
545 | } | ||
546 | |||
547 | |||
548 | /** | ||
549 | * Stop an active NAT test. | ||
550 | * | ||
551 | * @param tst test to stop. | ||
552 | */ | ||
553 | void | ||
554 | GNUNET_NAT_test_stop (struct GNUNET_NAT_Test *tst) | ||
555 | { | ||
556 | struct NatActivity *pos; | ||
557 | struct ClientActivity *cpos; | ||
558 | |||
559 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping NAT test\n"); | ||
560 | while (NULL != (cpos = tst->ca_head)) | ||
561 | { | ||
562 | GNUNET_CONTAINER_DLL_remove (tst->ca_head, tst->ca_tail, cpos); | ||
563 | GNUNET_MQ_destroy (cpos->mq); | ||
564 | GNUNET_free (cpos); | ||
565 | } | ||
566 | while (NULL != (pos = tst->na_head)) | ||
567 | { | ||
568 | GNUNET_CONTAINER_DLL_remove (tst->na_head, tst->na_tail, pos); | ||
569 | GNUNET_SCHEDULER_cancel (pos->rtask); | ||
570 | GNUNET_NETWORK_socket_close (pos->sock); | ||
571 | GNUNET_free (pos); | ||
572 | } | ||
573 | if (NULL != tst->ttask) | ||
574 | { | ||
575 | GNUNET_SCHEDULER_cancel (tst->ttask); | ||
576 | tst->ttask = NULL; | ||
577 | } | ||
578 | if (NULL != tst->ltask) | ||
579 | { | ||
580 | GNUNET_SCHEDULER_cancel (tst->ltask); | ||
581 | tst->ltask = NULL; | ||
582 | } | ||
583 | if (NULL != tst->lsock) | ||
584 | { | ||
585 | GNUNET_NETWORK_socket_close (tst->lsock); | ||
586 | tst->lsock = NULL; | ||
587 | } | ||
588 | if (NULL != tst->nat) | ||
589 | { | ||
590 | GNUNET_NAT_unregister (tst->nat); | ||
591 | tst->nat = NULL; | ||
592 | } | ||
593 | GNUNET_free (tst); | ||
594 | } | ||
595 | |||
596 | |||
597 | /* end of nat_test.c */ | ||
diff --git a/src/service/nat-auto/gnunet-service-nat-auto.c b/src/service/nat-auto/gnunet-service-nat-auto.c new file mode 100644 index 000000000..7d7c732a0 --- /dev/null +++ b/src/service/nat-auto/gnunet-service-nat-auto.c | |||
@@ -0,0 +1,488 @@ | |||
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-auto/gnunet-service-nat-auto.c | ||
23 | * @brief NAT autoconfiguration service | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - merge client handle and autoconfig context | ||
28 | * - implement "more" autoconfig: | ||
29 | * + re-work gnunet-nat-server & integrate! | ||
30 | * + integrate "legacy" code | ||
31 | * + test manually punched NAT (how?) | ||
32 | */ | ||
33 | #include "platform.h" | ||
34 | #include <math.h> | ||
35 | #include "gnunet_util_lib.h" | ||
36 | #include "gnunet_protocols.h" | ||
37 | #include "gnunet_signatures.h" | ||
38 | #include "gnunet_nat_service.h" | ||
39 | #include "gnunet_statistics_service.h" | ||
40 | #include "gnunet_resolver_service.h" | ||
41 | #include "nat-auto.h" | ||
42 | #include <gcrypt.h> | ||
43 | |||
44 | |||
45 | /** | ||
46 | * How long do we wait until we forcefully terminate autoconfiguration? | ||
47 | */ | ||
48 | #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
49 | GNUNET_TIME_UNIT_SECONDS, 5) | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Internal data structure we track for each of our clients. | ||
54 | */ | ||
55 | struct ClientHandle | ||
56 | { | ||
57 | /** | ||
58 | * Kept in a DLL. | ||
59 | */ | ||
60 | struct ClientHandle *next; | ||
61 | |||
62 | /** | ||
63 | * Kept in a DLL. | ||
64 | */ | ||
65 | struct ClientHandle *prev; | ||
66 | |||
67 | /** | ||
68 | * Underlying handle for this client with the service. | ||
69 | */ | ||
70 | struct GNUNET_SERVICE_Client *client; | ||
71 | |||
72 | /** | ||
73 | * Message queue for communicating with the client. | ||
74 | */ | ||
75 | struct GNUNET_MQ_Handle *mq; | ||
76 | }; | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Context for autoconfiguration operations. | ||
81 | */ | ||
82 | struct AutoconfigContext | ||
83 | { | ||
84 | /** | ||
85 | * Kept in a DLL. | ||
86 | */ | ||
87 | struct AutoconfigContext *prev; | ||
88 | |||
89 | /** | ||
90 | * Kept in a DLL. | ||
91 | */ | ||
92 | struct AutoconfigContext *next; | ||
93 | |||
94 | /** | ||
95 | * Which client asked the question. | ||
96 | */ | ||
97 | struct ClientHandle *ch; | ||
98 | |||
99 | /** | ||
100 | * Configuration we are creating. | ||
101 | */ | ||
102 | struct GNUNET_CONFIGURATION_Handle *c; | ||
103 | |||
104 | /** | ||
105 | * Original configuration (for diffing). | ||
106 | */ | ||
107 | struct GNUNET_CONFIGURATION_Handle *orig; | ||
108 | |||
109 | /** | ||
110 | * Timeout task to force termination. | ||
111 | */ | ||
112 | struct GNUNET_SCHEDULER_Task *timeout_task; | ||
113 | |||
114 | /** | ||
115 | * #GNUNET_YES if upnpc should be used, | ||
116 | * #GNUNET_NO if upnpc should not be used, | ||
117 | * #GNUNET_SYSERR if we should simply not change the option. | ||
118 | */ | ||
119 | int enable_upnpc; | ||
120 | |||
121 | /** | ||
122 | * Status code to return to the client. | ||
123 | */ | ||
124 | enum GNUNET_NAT_StatusCode status_code; | ||
125 | |||
126 | /** | ||
127 | * NAT type to return to the client. | ||
128 | */ | ||
129 | enum GNUNET_NAT_Type type; | ||
130 | }; | ||
131 | |||
132 | |||
133 | /** | ||
134 | * Head of client DLL. | ||
135 | */ | ||
136 | static struct ClientHandle *ch_head; | ||
137 | |||
138 | /** | ||
139 | * Tail of client DLL. | ||
140 | */ | ||
141 | static struct ClientHandle *ch_tail; | ||
142 | |||
143 | /** | ||
144 | * DLL of our autoconfiguration operations. | ||
145 | */ | ||
146 | static struct AutoconfigContext *ac_head; | ||
147 | |||
148 | /** | ||
149 | * DLL of our autoconfiguration operations. | ||
150 | */ | ||
151 | static struct AutoconfigContext *ac_tail; | ||
152 | |||
153 | /** | ||
154 | * Handle to our current configuration. | ||
155 | */ | ||
156 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
157 | |||
158 | /** | ||
159 | * Handle to the statistics service. | ||
160 | */ | ||
161 | static struct GNUNET_STATISTICS_Handle *stats; | ||
162 | |||
163 | |||
164 | /** | ||
165 | * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message | ||
166 | * from client. | ||
167 | * | ||
168 | * @param cls client who sent the message | ||
169 | * @param message the message received | ||
170 | * @return #GNUNET_OK if message is well-formed | ||
171 | */ | ||
172 | static int | ||
173 | check_autoconfig_request (void *cls, | ||
174 | const struct | ||
175 | GNUNET_NAT_AUTO_AutoconfigRequestMessage *message) | ||
176 | { | ||
177 | return GNUNET_OK; /* checked later */ | ||
178 | } | ||
179 | |||
180 | |||
181 | /** | ||
182 | * Stop all pending activities with respect to the @a ac | ||
183 | * | ||
184 | * @param ac autoconfiguration to terminate activities for | ||
185 | */ | ||
186 | static void | ||
187 | terminate_ac_activities (struct AutoconfigContext *ac) | ||
188 | { | ||
189 | if (NULL != ac->timeout_task) | ||
190 | { | ||
191 | GNUNET_SCHEDULER_cancel (ac->timeout_task); | ||
192 | ac->timeout_task = NULL; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | |||
197 | /** | ||
198 | * Finish handling the autoconfiguration request and send | ||
199 | * the response to the client. | ||
200 | * | ||
201 | * @param cls the `struct AutoconfigContext` to conclude | ||
202 | */ | ||
203 | static void | ||
204 | conclude_autoconfig_request (void *cls) | ||
205 | { | ||
206 | struct AutoconfigContext *ac = cls; | ||
207 | struct ClientHandle *ch = ac->ch; | ||
208 | struct GNUNET_NAT_AUTO_AutoconfigResultMessage *arm; | ||
209 | struct GNUNET_MQ_Envelope *env; | ||
210 | size_t c_size; | ||
211 | char *buf; | ||
212 | struct GNUNET_CONFIGURATION_Handle *diff; | ||
213 | |||
214 | ac->timeout_task = NULL; | ||
215 | terminate_ac_activities (ac); | ||
216 | |||
217 | /* Send back response */ | ||
218 | diff = GNUNET_CONFIGURATION_get_diff (ac->orig, | ||
219 | ac->c); | ||
220 | buf = GNUNET_CONFIGURATION_serialize (diff, | ||
221 | &c_size); | ||
222 | GNUNET_CONFIGURATION_destroy (diff); | ||
223 | env = GNUNET_MQ_msg_extra (arm, | ||
224 | c_size, | ||
225 | GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT); | ||
226 | arm->status_code = htonl ((uint32_t) ac->status_code); | ||
227 | arm->type = htonl ((uint32_t) ac->type); | ||
228 | GNUNET_memcpy (&arm[1], | ||
229 | buf, | ||
230 | c_size); | ||
231 | GNUNET_free (buf); | ||
232 | GNUNET_MQ_send (ch->mq, | ||
233 | env); | ||
234 | |||
235 | /* clean up */ | ||
236 | GNUNET_CONFIGURATION_destroy (ac->orig); | ||
237 | GNUNET_CONFIGURATION_destroy (ac->c); | ||
238 | GNUNET_CONTAINER_DLL_remove (ac_head, | ||
239 | ac_tail, | ||
240 | ac); | ||
241 | GNUNET_free (ac); | ||
242 | GNUNET_SERVICE_client_continue (ch->client); | ||
243 | } | ||
244 | |||
245 | |||
246 | /** | ||
247 | * Check if all autoconfiguration operations have concluded, | ||
248 | * and if they have, send the result back to the client. | ||
249 | * | ||
250 | * @param ac autoconfiguation context to check | ||
251 | */ | ||
252 | static void | ||
253 | check_autoconfig_finished (struct AutoconfigContext *ac) | ||
254 | { | ||
255 | GNUNET_SCHEDULER_cancel (ac->timeout_task); | ||
256 | ac->timeout_task | ||
257 | = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request, | ||
258 | ac); | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * Update ENABLE_UPNPC configuration option. | ||
264 | * | ||
265 | * @param ac autoconfiguration to update | ||
266 | */ | ||
267 | static void | ||
268 | update_enable_upnpc_option (struct AutoconfigContext *ac) | ||
269 | { | ||
270 | switch (ac->enable_upnpc) | ||
271 | { | ||
272 | case GNUNET_YES: | ||
273 | GNUNET_CONFIGURATION_set_value_string (ac->c, | ||
274 | "NAT", | ||
275 | "ENABLE_UPNP", | ||
276 | "YES"); | ||
277 | break; | ||
278 | |||
279 | case GNUNET_NO: | ||
280 | GNUNET_CONFIGURATION_set_value_string (ac->c, | ||
281 | "NAT", | ||
282 | "ENABLE_UPNP", | ||
283 | "NO"); | ||
284 | break; | ||
285 | |||
286 | case GNUNET_SYSERR: | ||
287 | /* We are unsure, do not change option */ | ||
288 | break; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | |||
293 | /** | ||
294 | * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from | ||
295 | * client. | ||
296 | * | ||
297 | * @param cls client who sent the message | ||
298 | * @param message the message received | ||
299 | */ | ||
300 | static void | ||
301 | handle_autoconfig_request (void *cls, | ||
302 | const struct | ||
303 | GNUNET_NAT_AUTO_AutoconfigRequestMessage *message) | ||
304 | { | ||
305 | struct ClientHandle *ch = cls; | ||
306 | size_t left = ntohs (message->header.size) - sizeof(*message); | ||
307 | struct AutoconfigContext *ac; | ||
308 | |||
309 | ac = GNUNET_new (struct AutoconfigContext); | ||
310 | ac->status_code = GNUNET_NAT_ERROR_SUCCESS; | ||
311 | ac->ch = ch; | ||
312 | ac->c = GNUNET_CONFIGURATION_create (); | ||
313 | if (GNUNET_OK != | ||
314 | GNUNET_CONFIGURATION_deserialize (ac->c, | ||
315 | (const char *) &message[1], | ||
316 | left, | ||
317 | NULL)) | ||
318 | { | ||
319 | GNUNET_break (0); | ||
320 | GNUNET_SERVICE_client_drop (ch->client); | ||
321 | GNUNET_CONFIGURATION_destroy (ac->c); | ||
322 | GNUNET_free (ac); | ||
323 | return; | ||
324 | } | ||
325 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
326 | "Received REQUEST_AUTO_CONFIG message from client\n"); | ||
327 | |||
328 | GNUNET_CONTAINER_DLL_insert (ac_head, | ||
329 | ac_tail, | ||
330 | ac); | ||
331 | ac->orig | ||
332 | = GNUNET_CONFIGURATION_dup (ac->c); | ||
333 | ac->timeout_task | ||
334 | = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT, | ||
335 | &conclude_autoconfig_request, | ||
336 | ac); | ||
337 | ac->enable_upnpc = GNUNET_SYSERR; /* undecided */ | ||
338 | |||
339 | /* Probe for upnpc */ | ||
340 | if (GNUNET_SYSERR == | ||
341 | GNUNET_OS_check_helper_binary ("upnpc", | ||
342 | GNUNET_NO, | ||
343 | NULL)) | ||
344 | { | ||
345 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
346 | _ ("UPnP client `upnpc` command not found, disabling UPnP\n")); | ||
347 | ac->enable_upnpc = GNUNET_NO; | ||
348 | } | ||
349 | else | ||
350 | { | ||
351 | /* We might at some point be behind NAT, try upnpc */ | ||
352 | ac->enable_upnpc = GNUNET_YES; | ||
353 | } | ||
354 | update_enable_upnpc_option (ac); | ||
355 | |||
356 | /* Finally, check if we are already done */ | ||
357 | check_autoconfig_finished (ac); | ||
358 | } | ||
359 | |||
360 | |||
361 | /** | ||
362 | * Task run during shutdown. | ||
363 | * | ||
364 | * @param cls unused | ||
365 | */ | ||
366 | static void | ||
367 | shutdown_task (void *cls) | ||
368 | { | ||
369 | struct AutoconfigContext *ac; | ||
370 | |||
371 | while (NULL != (ac = ac_head)) | ||
372 | { | ||
373 | GNUNET_CONTAINER_DLL_remove (ac_head, | ||
374 | ac_tail, | ||
375 | ac); | ||
376 | terminate_ac_activities (ac); | ||
377 | GNUNET_free (ac); | ||
378 | } | ||
379 | if (NULL != stats) | ||
380 | { | ||
381 | GNUNET_STATISTICS_destroy (stats, | ||
382 | GNUNET_NO); | ||
383 | stats = NULL; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | |||
388 | /** | ||
389 | * Setup NAT service. | ||
390 | * | ||
391 | * @param cls closure | ||
392 | * @param c configuration to use | ||
393 | * @param service the initialized service | ||
394 | */ | ||
395 | static void | ||
396 | run (void *cls, | ||
397 | const struct GNUNET_CONFIGURATION_Handle *c, | ||
398 | struct GNUNET_SERVICE_Handle *service) | ||
399 | { | ||
400 | cfg = c; | ||
401 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
402 | NULL); | ||
403 | stats = GNUNET_STATISTICS_create ("nat-auto", | ||
404 | cfg); | ||
405 | } | ||
406 | |||
407 | |||
408 | /** | ||
409 | * Callback called when a client connects to the service. | ||
410 | * | ||
411 | * @param cls closure for the service | ||
412 | * @param c the new client that connected to the service | ||
413 | * @param mq the message queue used to send messages to the client | ||
414 | * @return a `struct ClientHandle` | ||
415 | */ | ||
416 | static void * | ||
417 | client_connect_cb (void *cls, | ||
418 | struct GNUNET_SERVICE_Client *c, | ||
419 | struct GNUNET_MQ_Handle *mq) | ||
420 | { | ||
421 | struct ClientHandle *ch; | ||
422 | |||
423 | ch = GNUNET_new (struct ClientHandle); | ||
424 | ch->mq = mq; | ||
425 | ch->client = c; | ||
426 | GNUNET_CONTAINER_DLL_insert (ch_head, | ||
427 | ch_tail, | ||
428 | ch); | ||
429 | return ch; | ||
430 | } | ||
431 | |||
432 | |||
433 | /** | ||
434 | * Callback called when a client disconnected from the service | ||
435 | * | ||
436 | * @param cls closure for the service | ||
437 | * @param c the client that disconnected | ||
438 | * @param internal_cls a `struct ClientHandle *` | ||
439 | */ | ||
440 | static void | ||
441 | client_disconnect_cb (void *cls, | ||
442 | struct GNUNET_SERVICE_Client *c, | ||
443 | void *internal_cls) | ||
444 | { | ||
445 | struct ClientHandle *ch = internal_cls; | ||
446 | |||
447 | GNUNET_CONTAINER_DLL_remove (ch_head, | ||
448 | ch_tail, | ||
449 | ch); | ||
450 | GNUNET_free (ch); | ||
451 | } | ||
452 | |||
453 | |||
454 | /** | ||
455 | * Define "main" method using service macro. | ||
456 | */ | ||
457 | GNUNET_SERVICE_MAIN | ||
458 | ("nat-auto", | ||
459 | GNUNET_SERVICE_OPTION_NONE, | ||
460 | &run, | ||
461 | &client_connect_cb, | ||
462 | &client_disconnect_cb, | ||
463 | NULL, | ||
464 | GNUNET_MQ_hd_var_size (autoconfig_request, | ||
465 | GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG, | ||
466 | struct GNUNET_NAT_AUTO_AutoconfigRequestMessage, | ||
467 | NULL), | ||
468 | GNUNET_MQ_handler_end ()); | ||
469 | |||
470 | |||
471 | #if defined(__linux__) && defined(__GLIBC__) | ||
472 | #include <malloc.h> | ||
473 | |||
474 | /** | ||
475 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
476 | */ | ||
477 | void __attribute__ ((constructor)) | ||
478 | GNUNET_ARM_memory_init () | ||
479 | { | ||
480 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
481 | mallopt (M_TOP_PAD, 1 * 1024); | ||
482 | malloc_trim (0); | ||
483 | } | ||
484 | |||
485 | |||
486 | #endif | ||
487 | |||
488 | /* end of gnunet-service-nat.c */ | ||
diff --git a/src/service/nat-auto/gnunet-service-nat-auto_legacy.c b/src/service/nat-auto/gnunet-service-nat-auto_legacy.c new file mode 100644 index 000000000..de7910588 --- /dev/null +++ b/src/service/nat-auto/gnunet-service-nat-auto_legacy.c | |||
@@ -0,0 +1,1093 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 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 | * @file nat/nat_auto.c | ||
23 | * @brief functions for auto-configuration of the network | ||
24 | * @author Christian Grothoff | ||
25 | * @author Bruno Cabral | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_resolver_service.h" | ||
30 | #include "gnunet_nat_lib.h" | ||
31 | #include "nat.h" | ||
32 | |||
33 | #define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__) | ||
34 | |||
35 | |||
36 | /** | ||
37 | * How long do we wait for the NAT test to report success? | ||
38 | */ | ||
39 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) | ||
40 | |||
41 | #define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
42 | GNUNET_TIME_UNIT_SECONDS, 10) | ||
43 | |||
44 | /** | ||
45 | * Phases of the auto configuration. | ||
46 | */ | ||
47 | enum AutoPhase | ||
48 | { | ||
49 | /** | ||
50 | * Initial start value. | ||
51 | */ | ||
52 | AUTO_INIT = 0, | ||
53 | |||
54 | /** | ||
55 | * Test our external IP. | ||
56 | */ | ||
57 | AUTO_EXTERNAL_IP, | ||
58 | |||
59 | /** | ||
60 | * Test our external IP. | ||
61 | */ | ||
62 | AUTO_STUN, | ||
63 | |||
64 | /** | ||
65 | * Test our internal IP. | ||
66 | */ | ||
67 | AUTO_LOCAL_IP, | ||
68 | |||
69 | /** | ||
70 | * Test if NAT was punched. | ||
71 | */ | ||
72 | AUTO_NAT_PUNCHED, | ||
73 | |||
74 | /** | ||
75 | * Test if UPnP is working. | ||
76 | */ | ||
77 | AUTO_UPNPC, | ||
78 | |||
79 | /** | ||
80 | * Test if ICMP server works. | ||
81 | */ | ||
82 | AUTO_ICMP_SERVER, | ||
83 | |||
84 | /** | ||
85 | * Test if ICMP client works. | ||
86 | */ | ||
87 | AUTO_ICMP_CLIENT, | ||
88 | |||
89 | /** | ||
90 | * Last phase, we're done. | ||
91 | */ | ||
92 | AUTO_DONE | ||
93 | }; | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Handle to auto-configuration in progress. | ||
98 | */ | ||
99 | struct GNUNET_NAT_AutoHandle | ||
100 | { | ||
101 | /** | ||
102 | * Handle to the active NAT test. | ||
103 | */ | ||
104 | struct GNUNET_NAT_Test *tst; | ||
105 | |||
106 | /** | ||
107 | * Function to call when done. | ||
108 | */ | ||
109 | GNUNET_NAT_AutoResultCallback fin_cb; | ||
110 | |||
111 | /** | ||
112 | * Closure for @e fin_cb. | ||
113 | */ | ||
114 | void *fin_cb_cls; | ||
115 | |||
116 | /** | ||
117 | * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation. | ||
118 | */ | ||
119 | struct GNUNET_NAT_ExternalHandle *eh; | ||
120 | |||
121 | /** | ||
122 | * Current configuration (with updates from previous phases) | ||
123 | */ | ||
124 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
125 | |||
126 | /** | ||
127 | * Original configuration (used to calculate differences) | ||
128 | */ | ||
129 | struct GNUNET_CONFIGURATION_Handle *initial_cfg; | ||
130 | |||
131 | /** | ||
132 | * Task identifier for the timeout. | ||
133 | */ | ||
134 | struct GNUNET_SCHEDULER_Task *task; | ||
135 | |||
136 | /** | ||
137 | * Message queue to the gnunet-nat-server. | ||
138 | */ | ||
139 | struct GNUNET_MQ_Handle *mq; | ||
140 | |||
141 | /** | ||
142 | * Where are we in the test? | ||
143 | */ | ||
144 | enum AutoPhase phase; | ||
145 | |||
146 | /** | ||
147 | * Situation of the NAT | ||
148 | */ | ||
149 | enum GNUNET_NAT_Type type; | ||
150 | |||
151 | /** | ||
152 | * Do we have IPv6? | ||
153 | */ | ||
154 | int have_v6; | ||
155 | |||
156 | /** | ||
157 | * UPnP already set the external ip address ? | ||
158 | */ | ||
159 | int upnp_set_external_address; | ||
160 | |||
161 | /** | ||
162 | * Did the external server connected back ? | ||
163 | */ | ||
164 | int connected_back; | ||
165 | |||
166 | /** | ||
167 | * Address detected by STUN | ||
168 | */ | ||
169 | char *stun_ip; | ||
170 | |||
171 | unsigned int stun_port; | ||
172 | |||
173 | /** | ||
174 | * Internal IP is the same as the public one ? | ||
175 | */ | ||
176 | int internal_ip_is_public; | ||
177 | |||
178 | /** | ||
179 | * Error code for better debugging and user feedback | ||
180 | */ | ||
181 | enum GNUNET_NAT_StatusCode ret; | ||
182 | }; | ||
183 | |||
184 | |||
185 | /** | ||
186 | * The listen socket of the service for IPv4 | ||
187 | */ | ||
188 | static struct GNUNET_NETWORK_Handle *lsock4; | ||
189 | |||
190 | /** | ||
191 | * The listen task ID for IPv4 | ||
192 | */ | ||
193 | static struct GNUNET_SCHEDULER_Task *ltask4; | ||
194 | |||
195 | /** | ||
196 | * The port the test service is running on (default 7895) | ||
197 | */ | ||
198 | static unsigned long long port = 7895; | ||
199 | |||
200 | static char *stun_server = "stun.ekiga.net"; | ||
201 | |||
202 | static unsigned int stun_port = 3478; | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Run the next phase of the auto test. | ||
207 | * | ||
208 | * @param ah auto test handle | ||
209 | */ | ||
210 | static void | ||
211 | next_phase (struct GNUNET_NAT_AutoHandle *ah); | ||
212 | |||
213 | |||
214 | static void | ||
215 | process_stun_reply (struct sockaddr_in *answer, | ||
216 | struct GNUNET_NAT_AutoHandle *ah) | ||
217 | { | ||
218 | ah->stun_ip = inet_ntoa (answer->sin_addr); | ||
219 | ah->stun_port = ntohs (answer->sin_port); | ||
220 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
221 | "External IP is: %s , with port %u\n", | ||
222 | ah->stun_ip, | ||
223 | ah->stun_port); | ||
224 | next_phase (ah); | ||
225 | } | ||
226 | |||
227 | |||
228 | /** | ||
229 | * Function that terminates the test. | ||
230 | */ | ||
231 | static void | ||
232 | stop_stun () | ||
233 | { | ||
234 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
235 | "Stopping STUN and quitting...\n"); | ||
236 | /* Clean task */ | ||
237 | if (NULL != ltask4) | ||
238 | { | ||
239 | GNUNET_SCHEDULER_cancel (ltask4); | ||
240 | ltask4 = NULL; | ||
241 | } | ||
242 | /* Clean socket */ | ||
243 | if (NULL != lsock4) | ||
244 | { | ||
245 | GNUNET_NETWORK_socket_close (lsock4); | ||
246 | lsock4 = NULL; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Activity on our incoming socket. Read data from the | ||
253 | * incoming connection. | ||
254 | * | ||
255 | * @param cls | ||
256 | */ | ||
257 | static void | ||
258 | do_udp_read (void *cls) | ||
259 | { | ||
260 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
261 | unsigned char reply_buf[1024]; | ||
262 | ssize_t rlen; | ||
263 | struct sockaddr_in answer; | ||
264 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
265 | |||
266 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
267 | if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) && | ||
268 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, | ||
269 | lsock4))) | ||
270 | { | ||
271 | rlen = GNUNET_NETWORK_socket_recv (lsock4, | ||
272 | reply_buf, | ||
273 | sizeof(reply_buf)); | ||
274 | |||
275 | // Lets handle the packet | ||
276 | memset (&answer, 0, sizeof(struct sockaddr_in)); | ||
277 | if (ah->phase == AUTO_NAT_PUNCHED) | ||
278 | { | ||
279 | // Destroy the connection | ||
280 | GNUNET_NETWORK_socket_close (lsock4); | ||
281 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
282 | "The external server was able to connect back"); | ||
283 | ah->connected_back = GNUNET_YES; | ||
284 | next_phase (ah); | ||
285 | } | ||
286 | else | ||
287 | { | ||
288 | if (GNUNET_OK == | ||
289 | GNUNET_NAT_stun_handle_packet (reply_buf, rlen, &answer)) | ||
290 | { | ||
291 | // Process the answer | ||
292 | process_stun_reply (&answer, ah); | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | next_phase (ah); | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | else | ||
301 | { | ||
302 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
303 | "TIMEOUT while waiting for an answer\n"); | ||
304 | if (ah->phase == AUTO_NAT_PUNCHED) | ||
305 | { | ||
306 | stop_stun (); | ||
307 | } | ||
308 | |||
309 | next_phase (ah); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | |||
314 | /** | ||
315 | * Create an IPv4 listen socket bound to our port. | ||
316 | * | ||
317 | * @return NULL on error | ||
318 | */ | ||
319 | static struct GNUNET_NETWORK_Handle * | ||
320 | bind_v4 () | ||
321 | { | ||
322 | struct GNUNET_NETWORK_Handle *ls; | ||
323 | struct sockaddr_in sa4; | ||
324 | int eno; | ||
325 | |||
326 | memset (&sa4, 0, sizeof(sa4)); | ||
327 | sa4.sin_family = AF_INET; | ||
328 | sa4.sin_port = htons (port); | ||
329 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
330 | sa4.sin_len = sizeof(sa4); | ||
331 | #endif | ||
332 | ls = GNUNET_NETWORK_socket_create (AF_INET, | ||
333 | SOCK_DGRAM, | ||
334 | 0); | ||
335 | if (NULL == ls) | ||
336 | return NULL; | ||
337 | if (GNUNET_OK != | ||
338 | GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4, | ||
339 | sizeof(sa4))) | ||
340 | { | ||
341 | eno = errno; | ||
342 | GNUNET_NETWORK_socket_close (ls); | ||
343 | errno = eno; | ||
344 | return NULL; | ||
345 | } | ||
346 | return ls; | ||
347 | } | ||
348 | |||
349 | |||
350 | static void | ||
351 | request_callback (void *cls, | ||
352 | enum GNUNET_NAT_StatusCode result) | ||
353 | { | ||
354 | // struct GNUNET_NAT_AutoHandle *ah = cls; | ||
355 | |||
356 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
357 | "Request callback: stop and quit\n"); | ||
358 | stop_stun (); | ||
359 | |||
360 | // next_phase (ah); FIXME this always will be NULL, as called in test_stun() | ||
361 | } | ||
362 | |||
363 | |||
364 | /** | ||
365 | * Function called by NAT to report the outcome of the nat-test. | ||
366 | * Clean up and update GUI. | ||
367 | * | ||
368 | * @param cls the auto handle | ||
369 | * @param success currently always #GNUNET_OK | ||
370 | * @param emsg NULL on success, otherwise an error message | ||
371 | */ | ||
372 | static void | ||
373 | result_callback (void *cls, | ||
374 | enum GNUNET_NAT_StatusCode ret) | ||
375 | { | ||
376 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
377 | |||
378 | if (GNUNET_NAT_ERROR_SUCCESS == ret) | ||
379 | GNUNET_NAT_test_stop (ah->tst); | ||
380 | ah->tst = NULL; | ||
381 | ah->ret = ret; | ||
382 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
383 | GNUNET_NAT_ERROR_SUCCESS == ret | ||
384 | ? _ ("NAT traversal with ICMP Server succeeded.\n") | ||
385 | : _ ("NAT traversal with ICMP Server failed.\n")); | ||
386 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER", | ||
387 | GNUNET_NAT_ERROR_SUCCESS == ret ? | ||
388 | "NO" : "YES"); | ||
389 | next_phase (ah); | ||
390 | } | ||
391 | |||
392 | |||
393 | /** | ||
394 | * Main function for the connection reversal test. | ||
395 | * | ||
396 | * @param cls the `struct GNUNET_NAT_AutoHandle` | ||
397 | */ | ||
398 | static void | ||
399 | reversal_test (void *cls) | ||
400 | { | ||
401 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
402 | |||
403 | ah->task = NULL; | ||
404 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
405 | _ ("Testing connection reversal with ICMP server.\n")); | ||
406 | GNUNET_RESOLVER_connect (ah->cfg); | ||
407 | ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0, TIMEOUT, | ||
408 | &result_callback, ah); | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | ||
413 | * Set our external IPv4 address based on the UPnP. | ||
414 | * | ||
415 | * | ||
416 | * @param cls closure with our setup context | ||
417 | * @param addr the address, NULL on errors | ||
418 | * @param emsg NULL on success, otherwise an error message | ||
419 | */ | ||
420 | static void | ||
421 | set_external_ipv4 (void *cls, | ||
422 | const struct in_addr *addr, | ||
423 | enum GNUNET_NAT_StatusCode ret) | ||
424 | { | ||
425 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
426 | char buf[INET_ADDRSTRLEN]; | ||
427 | |||
428 | ah->eh = NULL; | ||
429 | ah->ret = ret; | ||
430 | if (GNUNET_NAT_ERROR_SUCCESS != ret) | ||
431 | { | ||
432 | next_phase (ah); | ||
433 | return; | ||
434 | } | ||
435 | /* enable 'behind nat' */ | ||
436 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
437 | _ ("Detected external IP `%s'\n"), | ||
438 | inet_ntop (AF_INET, | ||
439 | addr, | ||
440 | buf, | ||
441 | sizeof(buf))); | ||
442 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES"); | ||
443 | |||
444 | /* set external IP address */ | ||
445 | if (NULL == inet_ntop (AF_INET, addr, buf, sizeof(buf))) | ||
446 | { | ||
447 | GNUNET_break (0); | ||
448 | /* actually, this should never happen, as the caller already executed just | ||
449 | * this check, but for consistency (eg: future changes in the caller) | ||
450 | * we still need to report this error... | ||
451 | */ | ||
452 | ah->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; | ||
453 | next_phase (ah); | ||
454 | return; | ||
455 | } | ||
456 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS", | ||
457 | buf); | ||
458 | ah->upnp_set_external_address = GNUNET_YES; | ||
459 | next_phase (ah); | ||
460 | } | ||
461 | |||
462 | |||
463 | /** | ||
464 | * Determine our external IPv4 address. | ||
465 | * | ||
466 | * @param ah auto setup context | ||
467 | */ | ||
468 | static void | ||
469 | test_external_ip (struct GNUNET_NAT_AutoHandle *ah) | ||
470 | { | ||
471 | if (GNUNET_NAT_ERROR_SUCCESS != ah->ret) | ||
472 | next_phase (ah); | ||
473 | |||
474 | // FIXME: CPS? | ||
475 | /* try to detect external IP */ | ||
476 | ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT, | ||
477 | &set_external_ipv4, ah); | ||
478 | } | ||
479 | |||
480 | |||
481 | /** | ||
482 | * Determine our external IPv4 address and port using an external STUN server | ||
483 | * | ||
484 | * @param ah auto setup context | ||
485 | */ | ||
486 | static void | ||
487 | test_stun (struct GNUNET_NAT_AutoHandle *ah) | ||
488 | { | ||
489 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running STUN test\n"); | ||
490 | |||
491 | /* Get port from the configuration */ | ||
492 | if (GNUNET_OK != | ||
493 | GNUNET_CONFIGURATION_get_value_number (ah->cfg, | ||
494 | "transport-udp", | ||
495 | "PORT", | ||
496 | &port)) | ||
497 | { | ||
498 | port = 2086; | ||
499 | } | ||
500 | |||
501 | // Lets create the socket | ||
502 | lsock4 = bind_v4 (); | ||
503 | if (NULL == lsock4) | ||
504 | { | ||
505 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); | ||
506 | next_phase (ah); | ||
507 | return; | ||
508 | } | ||
509 | else | ||
510 | { | ||
511 | // Lets call our function now when it accepts | ||
512 | ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT, | ||
513 | lsock4, | ||
514 | &do_udp_read, | ||
515 | ah); | ||
516 | } | ||
517 | |||
518 | |||
519 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
520 | "STUN service listens on port %u\n", | ||
521 | (unsigned int) port); | ||
522 | if (GNUNET_NO == | ||
523 | GNUNET_NAT_stun_make_request (stun_server, | ||
524 | stun_port, | ||
525 | lsock4, | ||
526 | &request_callback, | ||
527 | NULL)) | ||
528 | { | ||
529 | /*An error happened*/ | ||
530 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN error, stopping\n"); | ||
531 | stop_stun (); | ||
532 | next_phase (ah); | ||
533 | } | ||
534 | } | ||
535 | |||
536 | |||
537 | /** | ||
538 | * Process list of local IP addresses. Find and set the | ||
539 | * one of the default interface. | ||
540 | * | ||
541 | * @param cls our `struct GNUNET_NAT_AutoHandle` | ||
542 | * @param name name of the interface (can be NULL for unknown) | ||
543 | * @param isDefault is this presumably the default interface | ||
544 | * @param addr address of this interface (can be NULL for unknown or unassigned) | ||
545 | * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) | ||
546 | * @param netmask the network mask (can be NULL for unknown or unassigned)) | ||
547 | * @param addrlen length of the @a addr and @a broadcast_addr | ||
548 | * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort | ||
549 | */ | ||
550 | static int | ||
551 | process_if (void *cls, | ||
552 | const char *name, | ||
553 | int isDefault, | ||
554 | const struct sockaddr *addr, | ||
555 | const struct sockaddr *broadcast_addr, | ||
556 | const struct sockaddr *netmask, | ||
557 | socklen_t addrlen) | ||
558 | { | ||
559 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
560 | const struct sockaddr_in *in; | ||
561 | char buf[INET_ADDRSTRLEN]; | ||
562 | |||
563 | |||
564 | if ((sizeof(struct sockaddr_in6) == addrlen) && | ||
565 | (0 != GNUNET_memcmp (&in6addr_loopback, &((const struct | ||
566 | sockaddr_in6 *) addr)-> | ||
567 | sin6_addr)) && | ||
568 | (! IN6_IS_ADDR_LINKLOCAL (&((const struct | ||
569 | sockaddr_in6 *) addr)->sin6_addr))) | ||
570 | { | ||
571 | ah->have_v6 = GNUNET_YES; | ||
572 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
573 | _ ( | ||
574 | "This system has a global IPv6 address, setting IPv6 to supported.\n")); | ||
575 | |||
576 | return GNUNET_OK; | ||
577 | } | ||
578 | if (addrlen != sizeof(struct sockaddr_in)) | ||
579 | return GNUNET_OK; | ||
580 | in = (const struct sockaddr_in *) addr; | ||
581 | |||
582 | |||
583 | /* set internal IP address */ | ||
584 | if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof(buf))) | ||
585 | { | ||
586 | GNUNET_break (0); | ||
587 | return GNUNET_OK; | ||
588 | } | ||
589 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS", | ||
590 | buf); | ||
591 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
592 | _ ("Detected internal network address `%s'.\n"), | ||
593 | buf); | ||
594 | |||
595 | |||
596 | ah->ret = GNUNET_NAT_ERROR_SUCCESS; | ||
597 | |||
598 | /* Check if our internal IP is the same as the External detect by STUN*/ | ||
599 | if (ah->stun_ip && (strcmp (buf, ah->stun_ip) == 0)) | ||
600 | { | ||
601 | ah->internal_ip_is_public = GNUNET_YES; | ||
602 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
603 | "A internal IP is the sameas the external"); | ||
604 | /* No need to continue*/ | ||
605 | return GNUNET_SYSERR; | ||
606 | } | ||
607 | |||
608 | /* no need to continue iteration if we found the default */ | ||
609 | if (! isDefault) | ||
610 | return GNUNET_OK; | ||
611 | else | ||
612 | return GNUNET_SYSERR; | ||
613 | } | ||
614 | |||
615 | |||
616 | /** | ||
617 | * Determine our local IP addresses; detect internal IP & IPv6-support | ||
618 | * | ||
619 | * @param ah auto setup context | ||
620 | */ | ||
621 | static void | ||
622 | test_local_ip (struct GNUNET_NAT_AutoHandle *ah) | ||
623 | { | ||
624 | ah->have_v6 = GNUNET_NO; | ||
625 | ah->ret = GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO; // reset to success if any of the IFs in below iterator has a valid IP | ||
626 | GNUNET_OS_network_interfaces_list (&process_if, ah); | ||
627 | |||
628 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6", | ||
629 | (GNUNET_YES == ah->have_v6) ? "NO" : | ||
630 | "YES"); | ||
631 | next_phase (ah); | ||
632 | } | ||
633 | |||
634 | |||
635 | /** | ||
636 | * We got disconnected from the NAT server. Stop | ||
637 | * waiting for a reply. | ||
638 | * | ||
639 | * @param cls the `struct GNUNET_NAT_AutoHandle` | ||
640 | * @param error error code | ||
641 | */ | ||
642 | static void | ||
643 | mq_error_handler (void *cls, | ||
644 | enum GNUNET_MQ_Error error) | ||
645 | { | ||
646 | struct GNUNET_NAT_AutoHandle *ah = cls; | ||
647 | |||
648 | GNUNET_MQ_destroy (ah->mq); | ||
649 | ah->mq = NULL; | ||
650 | /* wait a bit first? */ | ||
651 | next_phase (ah); | ||
652 | } | ||
653 | |||
654 | |||
655 | /** | ||
656 | * Test if NAT has been punched | ||
657 | * | ||
658 | * @param ah auto setup context | ||
659 | */ | ||
660 | static void | ||
661 | test_nat_punched (struct GNUNET_NAT_AutoHandle *ah) | ||
662 | { | ||
663 | struct GNUNET_NAT_TestMessage *msg; | ||
664 | struct GNUNET_MQ_Envelope *env; | ||
665 | |||
666 | if (! ah->stun_ip) | ||
667 | { | ||
668 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
669 | "We don't have a STUN IP"); | ||
670 | next_phase (ah); | ||
671 | return; | ||
672 | } | ||
673 | |||
674 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
675 | "Asking gnunet-nat-server to connect to `%s'\n", | ||
676 | ah->stun_ip); | ||
677 | ah->mq = GNUNET_CLIENT_connect (ah->cfg, | ||
678 | "gnunet-nat-server", | ||
679 | NULL, | ||
680 | &mq_error_handler, | ||
681 | ah); | ||
682 | if (NULL == ah->mq) | ||
683 | { | ||
684 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
685 | _ ("Failed to connect to `gnunet-nat-server'\n")); | ||
686 | next_phase (ah); | ||
687 | return; | ||
688 | } | ||
689 | env = GNUNET_MQ_msg (msg, | ||
690 | GNUNET_MESSAGE_TYPE_NAT_TEST); | ||
691 | msg->dst_ipv4 = inet_addr (ah->stun_ip); | ||
692 | msg->dport = htons (ah->stun_port); | ||
693 | msg->data = port; | ||
694 | msg->is_tcp = htonl ((uint32_t) GNUNET_NO); | ||
695 | GNUNET_MQ_send (ah->mq, | ||
696 | env); | ||
697 | if (NULL != ltask4) | ||
698 | { | ||
699 | GNUNET_SCHEDULER_cancel (ltask4); | ||
700 | ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT, | ||
701 | lsock4, | ||
702 | &do_udp_read, | ||
703 | ah); | ||
704 | } | ||
705 | } | ||
706 | |||
707 | |||
708 | /** | ||
709 | * Test if UPnPC works. | ||
710 | * | ||
711 | * @param ah auto setup context | ||
712 | */ | ||
713 | static void | ||
714 | test_upnpc (struct GNUNET_NAT_AutoHandle *ah) | ||
715 | { | ||
716 | int have_upnpc; | ||
717 | |||
718 | if (GNUNET_NAT_ERROR_SUCCESS != ah->ret) | ||
719 | next_phase (ah); | ||
720 | |||
721 | // test if upnpc is available | ||
722 | have_upnpc = (GNUNET_SYSERR != | ||
723 | GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL)); | ||
724 | // FIXME: test if upnpc is actually working, that is, if transports start to work once we use UPnP | ||
725 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
726 | (have_upnpc) | ||
727 | ? _ ("upnpc found, enabling its use\n") | ||
728 | : _ ("upnpc not found\n")); | ||
729 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP", | ||
730 | (GNUNET_YES == have_upnpc) ? "YES" : | ||
731 | "NO"); | ||
732 | next_phase (ah); | ||
733 | } | ||
734 | |||
735 | |||
736 | /** | ||
737 | * Test if ICMP server is working | ||
738 | * | ||
739 | * @param ah auto setup context | ||
740 | */ | ||
741 | static void | ||
742 | test_icmp_server (struct GNUNET_NAT_AutoHandle *ah) | ||
743 | { | ||
744 | int ext_ip; | ||
745 | int nated; | ||
746 | int binary; | ||
747 | char *tmp; | ||
748 | char *helper; | ||
749 | |||
750 | ext_ip = GNUNET_NO; | ||
751 | nated = GNUNET_NO; | ||
752 | binary = GNUNET_NO; | ||
753 | |||
754 | tmp = NULL; | ||
755 | helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server"); | ||
756 | if ((GNUNET_OK == | ||
757 | GNUNET_CONFIGURATION_get_value_string (ah->cfg, | ||
758 | "nat", | ||
759 | "EXTERNAL_ADDRESS", | ||
760 | &tmp)) && | ||
761 | (0 < strlen (tmp))) | ||
762 | { | ||
763 | ext_ip = GNUNET_OK; | ||
764 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
765 | _ ( | ||
766 | "test_icmp_server not possible, as we have no public IPv4 address\n")); | ||
767 | } | ||
768 | else | ||
769 | goto err; | ||
770 | |||
771 | if (GNUNET_YES == | ||
772 | GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, | ||
773 | "nat", | ||
774 | "BEHIND_NAT")) | ||
775 | { | ||
776 | nated = GNUNET_YES; | ||
777 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
778 | _ ( | ||
779 | "test_icmp_server not possible, as we are not behind NAT\n")); | ||
780 | } | ||
781 | else | ||
782 | goto err; | ||
783 | |||
784 | if (GNUNET_YES == | ||
785 | GNUNET_OS_check_helper_binary (helper, | ||
786 | GNUNET_YES, | ||
787 | "-d 127.0.0.1")) | ||
788 | { | ||
789 | binary = GNUNET_OK; // use localhost as source for that one udp-port, ok for testing | ||
790 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
791 | _ ("No working gnunet-helper-nat-server found\n")); | ||
792 | } | ||
793 | err: | ||
794 | GNUNET_free (tmp); | ||
795 | GNUNET_free (helper); | ||
796 | |||
797 | if ((GNUNET_OK == ext_ip) && | ||
798 | (GNUNET_YES == nated) && | ||
799 | (GNUNET_OK == binary)) | ||
800 | ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, | ||
801 | ah); | ||
802 | else | ||
803 | next_phase (ah); | ||
804 | } | ||
805 | |||
806 | |||
807 | /** | ||
808 | * Test if ICMP client is working | ||
809 | * | ||
810 | * @param ah auto setup context | ||
811 | */ | ||
812 | static void | ||
813 | test_icmp_client (struct GNUNET_NAT_AutoHandle *ah) | ||
814 | { | ||
815 | char *tmp; | ||
816 | char *helper; | ||
817 | |||
818 | tmp = NULL; | ||
819 | helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client"); | ||
820 | if ((GNUNET_OK == | ||
821 | GNUNET_CONFIGURATION_get_value_string (ah->cfg, | ||
822 | "nat", | ||
823 | "INTERNAL_ADDRESS", | ||
824 | &tmp)) && | ||
825 | (0 < strlen (tmp))) | ||
826 | { | ||
827 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
828 | _ ( | ||
829 | "test_icmp_client not possible, as we have no internal IPv4 address\n")); | ||
830 | } | ||
831 | else | ||
832 | goto err; | ||
833 | |||
834 | if (GNUNET_YES != | ||
835 | GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, | ||
836 | "nat", | ||
837 | "BEHIND_NAT")) | ||
838 | { | ||
839 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
840 | _ ( | ||
841 | "test_icmp_server not possible, as we are not behind NAT\n")); | ||
842 | } | ||
843 | else | ||
844 | goto err; | ||
845 | |||
846 | if (GNUNET_YES == | ||
847 | GNUNET_OS_check_helper_binary (helper, | ||
848 | GNUNET_YES, | ||
849 | "-d 127.0.0.1 127.0.0.2 42")) | ||
850 | { | ||
851 | // none of these parameters are actually used in privilege testing mode | ||
852 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
853 | _ ("No working gnunet-helper-nat-server found\n")); | ||
854 | } | ||
855 | err: | ||
856 | GNUNET_free (tmp); | ||
857 | GNUNET_free (helper); | ||
858 | |||
859 | next_phase (ah); | ||
860 | } | ||
861 | |||
862 | |||
863 | /** | ||
864 | * Run the next phase of the auto test. | ||
865 | */ | ||
866 | static void | ||
867 | next_phase (struct GNUNET_NAT_AutoHandle *ah) | ||
868 | { | ||
869 | struct GNUNET_CONFIGURATION_Handle *diff; | ||
870 | |||
871 | ah->phase++; | ||
872 | switch (ah->phase) | ||
873 | { | ||
874 | case AUTO_INIT: | ||
875 | GNUNET_assert (0); | ||
876 | break; | ||
877 | |||
878 | case AUTO_EXTERNAL_IP: | ||
879 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
880 | "Will run AUTO_EXTERNAL_IP\n"); | ||
881 | test_external_ip (ah); | ||
882 | break; | ||
883 | |||
884 | case AUTO_STUN: | ||
885 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
886 | "Will run AUTO_STUN\n"); | ||
887 | test_stun (ah); | ||
888 | break; | ||
889 | |||
890 | case AUTO_LOCAL_IP: | ||
891 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
892 | "Will run AUTO_LOCAL_IP\n"); | ||
893 | test_local_ip (ah); | ||
894 | break; | ||
895 | |||
896 | case AUTO_NAT_PUNCHED: | ||
897 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
898 | "Will run AUTO_NAT_PUNCHED\n"); | ||
899 | test_nat_punched (ah); | ||
900 | break; | ||
901 | |||
902 | case AUTO_UPNPC: | ||
903 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
904 | "Will run AUTO_UPNPC\n"); | ||
905 | test_upnpc (ah); | ||
906 | break; | ||
907 | |||
908 | case AUTO_ICMP_SERVER: | ||
909 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
910 | "Will run AUTO_ICMP_SERVER\n"); | ||
911 | test_icmp_server (ah); | ||
912 | break; | ||
913 | |||
914 | case AUTO_ICMP_CLIENT: | ||
915 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
916 | "Will run AUTO_ICMP_CLIENT\n"); | ||
917 | test_icmp_client (ah); | ||
918 | break; | ||
919 | |||
920 | case AUTO_DONE: | ||
921 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
922 | "Done with tests\n"); | ||
923 | if (! ah->internal_ip_is_public) | ||
924 | { | ||
925 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
926 | "nat", | ||
927 | "BEHIND_NAT", | ||
928 | "YES"); | ||
929 | |||
930 | if (ah->connected_back) | ||
931 | { | ||
932 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
933 | "nat", | ||
934 | "PUNCHED_NAT", | ||
935 | "YES"); | ||
936 | } | ||
937 | else | ||
938 | { | ||
939 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
940 | "nat", | ||
941 | "PUNCHED_NAT", | ||
942 | "NO"); | ||
943 | } | ||
944 | |||
945 | if (ah->stun_ip) | ||
946 | { | ||
947 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
948 | "nat", | ||
949 | "EXTERNAL_ADDRESS", | ||
950 | ah->stun_ip); | ||
951 | if (ah->connected_back) | ||
952 | { | ||
953 | ah->type = GNUNET_NAT_TYPE_STUN_PUNCHED_NAT; | ||
954 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
955 | "nat", | ||
956 | "USE_STUN", | ||
957 | "YES"); | ||
958 | } | ||
959 | else | ||
960 | { | ||
961 | ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT; | ||
962 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
963 | "nat", | ||
964 | "USE_STUN", | ||
965 | "NO"); | ||
966 | } | ||
967 | } | ||
968 | if (0 != ah->stun_port) | ||
969 | { | ||
970 | GNUNET_CONFIGURATION_set_value_number (ah->cfg, | ||
971 | "transport-udp", | ||
972 | "ADVERTISED_PORT", | ||
973 | ah->stun_port); | ||
974 | } | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | // The internal IP is the same as public, but we didn't got a incoming connection | ||
979 | if (ah->connected_back) | ||
980 | { | ||
981 | ah->type = GNUNET_NAT_TYPE_NO_NAT; | ||
982 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
983 | "nat", | ||
984 | "BEHIND_NAT", | ||
985 | "NO"); | ||
986 | } | ||
987 | else | ||
988 | { | ||
989 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
990 | "nat", | ||
991 | "BEHIND_NAT", | ||
992 | "YES"); | ||
993 | ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT; | ||
994 | if (ah->stun_ip) | ||
995 | { | ||
996 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
997 | "nat", | ||
998 | "EXTERNAL_ADDRESS", | ||
999 | ah->stun_ip); | ||
1000 | } | ||
1001 | if (0 != ah->stun_port) | ||
1002 | { | ||
1003 | GNUNET_CONFIGURATION_set_value_number (ah->cfg, | ||
1004 | "transport-udp", | ||
1005 | "ADVERTISED_PORT", | ||
1006 | ah->stun_port); | ||
1007 | } | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg, | ||
1012 | ah->cfg); | ||
1013 | |||
1014 | |||
1015 | ah->fin_cb (ah->fin_cb_cls, | ||
1016 | diff, | ||
1017 | ah->ret, | ||
1018 | ah->type); | ||
1019 | GNUNET_CONFIGURATION_destroy (diff); | ||
1020 | GNUNET_NAT_autoconfig_cancel (ah); | ||
1021 | } | ||
1022 | } | ||
1023 | |||
1024 | |||
1025 | /** | ||
1026 | * Start auto-configuration routine. The resolver service should | ||
1027 | * be available when this function is called. | ||
1028 | * | ||
1029 | * @param cfg initial configuration | ||
1030 | * @param cb function to call with autoconfiguration result | ||
1031 | * @param cb_cls closure for @a cb | ||
1032 | * @return handle to cancel operation | ||
1033 | */ | ||
1034 | struct GNUNET_NAT_AutoHandle * | ||
1035 | GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1036 | GNUNET_NAT_AutoResultCallback cb, | ||
1037 | void *cb_cls) | ||
1038 | { | ||
1039 | struct GNUNET_NAT_AutoHandle *ah; | ||
1040 | |||
1041 | ah = GNUNET_new (struct GNUNET_NAT_AutoHandle); | ||
1042 | ah->fin_cb = cb; | ||
1043 | ah->fin_cb_cls = cb_cls; | ||
1044 | ah->ret = GNUNET_NAT_ERROR_SUCCESS; | ||
1045 | ah->cfg = GNUNET_CONFIGURATION_dup (cfg); | ||
1046 | ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg); | ||
1047 | |||
1048 | /* never use loopback addresses if user wanted autoconfiguration */ | ||
1049 | GNUNET_CONFIGURATION_set_value_string (ah->cfg, | ||
1050 | "nat", | ||
1051 | "USE_LOCALADDR", | ||
1052 | "NO"); | ||
1053 | |||
1054 | next_phase (ah); | ||
1055 | return ah; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | /** | ||
1060 | * Abort autoconfiguration. | ||
1061 | * | ||
1062 | * @param ah handle for operation to abort | ||
1063 | */ | ||
1064 | void | ||
1065 | GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah) | ||
1066 | { | ||
1067 | if (NULL != ah->tst) | ||
1068 | { | ||
1069 | GNUNET_NAT_test_stop (ah->tst); | ||
1070 | ah->tst = NULL; | ||
1071 | } | ||
1072 | if (NULL != ah->eh) | ||
1073 | { | ||
1074 | GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh); | ||
1075 | ah->eh = NULL; | ||
1076 | } | ||
1077 | if (NULL != ah->mq) | ||
1078 | { | ||
1079 | GNUNET_MQ_destroy (ah->mq); | ||
1080 | ah->mq = NULL; | ||
1081 | } | ||
1082 | if (NULL != ah->task) | ||
1083 | { | ||
1084 | GNUNET_SCHEDULER_cancel (ah->task); | ||
1085 | ah->task = NULL; | ||
1086 | } | ||
1087 | GNUNET_CONFIGURATION_destroy (ah->cfg); | ||
1088 | GNUNET_CONFIGURATION_destroy (ah->initial_cfg); | ||
1089 | GNUNET_free (ah); | ||
1090 | } | ||
1091 | |||
1092 | |||
1093 | /* end of nat_auto.c */ | ||
diff --git a/src/service/nat-auto/meson.build b/src/service/nat-auto/meson.build new file mode 100644 index 000000000..f0e780254 --- /dev/null +++ b/src/service/nat-auto/meson.build | |||
@@ -0,0 +1,36 @@ | |||
1 | libgnunetnatauto_src = ['nat_auto_api.c', | ||
2 | 'nat_auto_api_test.c'] | ||
3 | |||
4 | gnunetservicenatauto_src = ['gnunet-service-nat-auto.c'] | ||
5 | |||
6 | configure_file(input : 'nat-auto.conf.in', | ||
7 | output : 'nat-auto.conf', | ||
8 | configuration : cdata, | ||
9 | install: true, | ||
10 | install_dir: pkgcfgdir) | ||
11 | |||
12 | if get_option('monolith') | ||
13 | foreach p : libgnunetnatauto_src + gnunetservicenatauto_src | ||
14 | gnunet_src += 'nat-auto/' + p | ||
15 | endforeach | ||
16 | endif | ||
17 | |||
18 | libgnunetnatauto = library('gnunetnatauto', | ||
19 | libgnunetnatauto_src, | ||
20 | soversion: '0', | ||
21 | version: '0.0.0', | ||
22 | dependencies: [libgnunetutil_dep, libgnunetnat_dep], | ||
23 | include_directories: [incdir, configuration_inc], | ||
24 | install: true, | ||
25 | install_dir: get_option('libdir')) | ||
26 | libgnunetnatauto_dep = declare_dependency(link_with : libgnunetnatauto) | ||
27 | |||
28 | executable ('gnunet-service-nat-auto', | ||
29 | gnunetservicenatauto_src, | ||
30 | dependencies: [libgnunetnatauto_dep, libgnunetutil_dep, | ||
31 | libgnunetnat_dep, | ||
32 | libgnunetstatistics_dep], | ||
33 | include_directories: [incdir, configuration_inc], | ||
34 | install: true, | ||
35 | install_dir: get_option('libdir')/'gnunet'/'libexec') | ||
36 | |||
diff --git a/src/service/nat-auto/nat-auto.conf.in b/src/service/nat-auto/nat-auto.conf.in new file mode 100644 index 000000000..d5f5c4eef --- /dev/null +++ b/src/service/nat-auto/nat-auto.conf.in | |||
@@ -0,0 +1,15 @@ | |||
1 | [nat-auto] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @UNIXONLY@ PORT = 2124 | ||
4 | HOSTNAME = localhost | ||
5 | BINARY = gnunet-service-nat-auto | ||
6 | ACCEPT_FROM = 127.0.0.1; | ||
7 | ACCEPT_FROM6 = ::1; | ||
8 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat-auto.sock | ||
9 | UNIX_MATCH_UID = YES | ||
10 | UNIX_MATCH_GID = YES | ||
11 | |||
12 | [gnunet-nat-server] | ||
13 | HOSTNAME = gnunet.org | ||
14 | PORT = 5724 | ||
15 | NOARMBIND = YES | ||
diff --git a/src/service/nat-auto/nat-auto.h b/src/service/nat-auto/nat-auto.h new file mode 100644 index 000000000..ac0e7c2d5 --- /dev/null +++ b/src/service/nat-auto/nat-auto.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011, 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 src/nat-auto/nat-auto.h | ||
23 | * @brief Messages for interaction with gnunet-nat-auto-service | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | */ | ||
27 | #ifndef NAT_AUTO_H | ||
28 | #define NAT_AUTO_H | ||
29 | #include "gnunet_util_lib.h" | ||
30 | |||
31 | |||
32 | GNUNET_NETWORK_STRUCT_BEGIN | ||
33 | |||
34 | /** | ||
35 | * Request to test NAT traversal, sent to the gnunet-nat-server | ||
36 | * (not the service!). | ||
37 | */ | ||
38 | struct GNUNET_NAT_AUTO_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 | * Client requesting automatic configuration. | ||
69 | */ | ||
70 | struct GNUNET_NAT_AUTO_AutoconfigRequestMessage | ||
71 | { | ||
72 | /** | ||
73 | * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG | ||
74 | */ | ||
75 | struct GNUNET_MessageHeader header; | ||
76 | |||
77 | /* Followed by configuration (diff, serialized, compressed) */ | ||
78 | }; | ||
79 | |||
80 | |||
81 | /** | ||
82 | * Service responding with proposed configuration. | ||
83 | */ | ||
84 | struct GNUNET_NAT_AUTO_AutoconfigResultMessage | ||
85 | { | ||
86 | /** | ||
87 | * Header with type #GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT | ||
88 | */ | ||
89 | struct GNUNET_MessageHeader header; | ||
90 | |||
91 | /** | ||
92 | * An `enum GNUNET_NAT_StatusCode` in NBO. | ||
93 | */ | ||
94 | int32_t status_code GNUNET_PACKED; | ||
95 | |||
96 | /** | ||
97 | * An `enum GNUNET_NAT_Type` in NBO. | ||
98 | */ | ||
99 | int32_t type GNUNET_PACKED; | ||
100 | |||
101 | /* Followed by configuration (diff, serialized, compressed) */ | ||
102 | }; | ||
103 | |||
104 | |||
105 | GNUNET_NETWORK_STRUCT_END | ||
106 | |||
107 | #endif | ||
diff --git a/src/service/nat-auto/nat_auto_api.c b/src/service/nat-auto/nat_auto_api.c new file mode 100644 index 000000000..3c982f883 --- /dev/null +++ b/src/service/nat-auto/nat_auto_api.c | |||
@@ -0,0 +1,292 @@ | |||
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-auto/nat_auto_api.c | ||
26 | * Routines for NAT auto configuration. | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | #include "gnunet_nat_service.h" | ||
30 | #include "gnunet_nat_auto_service.h" | ||
31 | #include "nat-auto.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Handle to auto-configuration in progress. | ||
36 | */ | ||
37 | struct GNUNET_NAT_AUTO_AutoHandle | ||
38 | { | ||
39 | /** | ||
40 | * Configuration we use. | ||
41 | */ | ||
42 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
43 | |||
44 | /** | ||
45 | * Message queue for communicating with the NAT service. | ||
46 | */ | ||
47 | struct GNUNET_MQ_Handle *mq; | ||
48 | |||
49 | /** | ||
50 | * Function called with the result from the autoconfiguration. | ||
51 | */ | ||
52 | GNUNET_NAT_AUTO_AutoResultCallback arc; | ||
53 | |||
54 | /** | ||
55 | * Closure for @e arc. | ||
56 | */ | ||
57 | void *arc_cls; | ||
58 | }; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Converts `enum GNUNET_NAT_StatusCode` to string | ||
63 | * | ||
64 | * @param err error code to resolve to a string | ||
65 | * @return point to a static string containing the error code | ||
66 | */ | ||
67 | const char * | ||
68 | GNUNET_NAT_AUTO_status2string (enum GNUNET_NAT_StatusCode err) | ||
69 | { | ||
70 | switch (err) | ||
71 | { | ||
72 | case GNUNET_NAT_ERROR_SUCCESS: | ||
73 | return _ ("Operation Successful"); | ||
74 | |||
75 | case GNUNET_NAT_ERROR_IPC_FAILURE: | ||
76 | return _ ("IPC failure"); | ||
77 | |||
78 | case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR: | ||
79 | return _ ("Failure in network subsystem, check permissions."); | ||
80 | |||
81 | case GNUNET_NAT_ERROR_TIMEOUT: | ||
82 | return _ ("Encountered timeout while performing operation"); | ||
83 | |||
84 | case GNUNET_NAT_ERROR_NOT_ONLINE: | ||
85 | return _ ("detected that we are offline"); | ||
86 | |||
87 | case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: | ||
88 | return _ ("`upnpc` command not found"); | ||
89 | |||
90 | case GNUNET_NAT_ERROR_UPNPC_FAILED: | ||
91 | return _ ("Failed to run `upnpc` command"); | ||
92 | |||
93 | case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: | ||
94 | return _ ("`upnpc' command took too long, process killed"); | ||
95 | |||
96 | case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: | ||
97 | return _ ("`upnpc' command failed to establish port mapping"); | ||
98 | |||
99 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: | ||
100 | return _ ("`external-ip' command not found"); | ||
101 | |||
102 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: | ||
103 | return _ ("Failed to run `external-ip` command"); | ||
104 | |||
105 | case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: | ||
106 | return _ ("`external-ip' command output invalid"); | ||
107 | |||
108 | case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: | ||
109 | return _ ("no valid address was returned by `external-ip'"); | ||
110 | |||
111 | case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO: | ||
112 | return _ ( | ||
113 | "Could not determine interface with internal/local network address"); | ||
114 | |||
115 | case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND: | ||
116 | return _ ("No functioning gnunet-helper-nat-server installation found"); | ||
117 | |||
118 | case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED: | ||
119 | return _ ("NAT test could not be initialized"); | ||
120 | |||
121 | case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT: | ||
122 | return _ ("NAT test timeout reached"); | ||
123 | |||
124 | case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED: | ||
125 | return _ ("could not register NAT"); | ||
126 | |||
127 | case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND: | ||
128 | return _ ("No working gnunet-helper-nat-client installation found"); | ||
129 | |||
130 | default: | ||
131 | return "unknown status code"; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Check result from autoconfiguration attempt. | ||
138 | * | ||
139 | * @param cls the `struct GNUNET_NAT_AUTO_AutoHandle` | ||
140 | * @param res the result | ||
141 | * @return #GNUNET_OK if @a res is well-formed (always for now) | ||
142 | */ | ||
143 | static int | ||
144 | check_auto_result (void *cls, | ||
145 | const struct GNUNET_NAT_AUTO_AutoconfigResultMessage *res) | ||
146 | { | ||
147 | return GNUNET_OK; | ||
148 | } | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Handle result from autoconfiguration attempt. | ||
153 | * | ||
154 | * @param cls the `struct GNUNET_NAT_AUTO_AutoHandle` | ||
155 | * @param res the result | ||
156 | */ | ||
157 | static void | ||
158 | handle_auto_result (void *cls, | ||
159 | const struct GNUNET_NAT_AUTO_AutoconfigResultMessage *res) | ||
160 | { | ||
161 | struct GNUNET_NAT_AUTO_AutoHandle *ah = cls; | ||
162 | size_t left; | ||
163 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
164 | enum GNUNET_NAT_Type type | ||
165 | = (enum GNUNET_NAT_Type) ntohl (res->type); | ||
166 | enum GNUNET_NAT_StatusCode status | ||
167 | = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code); | ||
168 | |||
169 | left = ntohs (res->header.size) - sizeof(*res); | ||
170 | cfg = GNUNET_CONFIGURATION_create (); | ||
171 | if (GNUNET_OK != | ||
172 | GNUNET_CONFIGURATION_deserialize (cfg, | ||
173 | (const char *) &res[1], | ||
174 | left, | ||
175 | NULL)) | ||
176 | { | ||
177 | GNUNET_break (0); | ||
178 | ah->arc (ah->arc_cls, | ||
179 | NULL, | ||
180 | GNUNET_NAT_ERROR_IPC_FAILURE, | ||
181 | type); | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | ah->arc (ah->arc_cls, | ||
186 | cfg, | ||
187 | status, | ||
188 | type); | ||
189 | } | ||
190 | GNUNET_CONFIGURATION_destroy (cfg); | ||
191 | GNUNET_NAT_AUTO_autoconfig_cancel (ah); | ||
192 | } | ||
193 | |||
194 | |||
195 | /** | ||
196 | * Handle queue errors by reporting autoconfiguration failure. | ||
197 | * | ||
198 | * @param cls the `struct GNUNET_NAT_AUTO_AutoHandle *` | ||
199 | * @param error details about the error | ||
200 | */ | ||
201 | static void | ||
202 | ah_error_handler (void *cls, | ||
203 | enum GNUNET_MQ_Error error) | ||
204 | { | ||
205 | struct GNUNET_NAT_AUTO_AutoHandle *ah = cls; | ||
206 | |||
207 | ah->arc (ah->arc_cls, | ||
208 | NULL, | ||
209 | GNUNET_NAT_ERROR_IPC_FAILURE, | ||
210 | GNUNET_NAT_TYPE_UNKNOWN); | ||
211 | GNUNET_NAT_AUTO_autoconfig_cancel (ah); | ||
212 | } | ||
213 | |||
214 | |||
215 | /** | ||
216 | * Start auto-configuration routine. The transport adapters should | ||
217 | * be stopped while this function is called. | ||
218 | * | ||
219 | * @param cfg initial configuration | ||
220 | * @param cb function to call with autoconfiguration result | ||
221 | * @param cb_cls closure for @a cb | ||
222 | * @return handle to cancel operation | ||
223 | */ | ||
224 | struct GNUNET_NAT_AUTO_AutoHandle * | ||
225 | GNUNET_NAT_AUTO_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
226 | GNUNET_NAT_AUTO_AutoResultCallback cb, | ||
227 | void *cb_cls) | ||
228 | { | ||
229 | struct GNUNET_NAT_AUTO_AutoHandle *ah = GNUNET_new (struct | ||
230 | GNUNET_NAT_AUTO_AutoHandle); | ||
231 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
232 | GNUNET_MQ_hd_var_size (auto_result, | ||
233 | GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT, | ||
234 | struct GNUNET_NAT_AUTO_AutoconfigResultMessage, | ||
235 | ah), | ||
236 | GNUNET_MQ_handler_end () | ||
237 | }; | ||
238 | struct GNUNET_MQ_Envelope *env; | ||
239 | struct GNUNET_NAT_AUTO_AutoconfigRequestMessage *req; | ||
240 | char *buf; | ||
241 | size_t size; | ||
242 | |||
243 | buf = GNUNET_CONFIGURATION_serialize (cfg, | ||
244 | &size); | ||
245 | if (size > GNUNET_MAX_MESSAGE_SIZE - sizeof(*req)) | ||
246 | { | ||
247 | GNUNET_break (0); | ||
248 | GNUNET_free (buf); | ||
249 | GNUNET_free (ah); | ||
250 | return NULL; | ||
251 | } | ||
252 | ah->arc = cb; | ||
253 | ah->arc_cls = cb_cls; | ||
254 | ah->mq = GNUNET_CLIENT_connect (cfg, | ||
255 | "nat", | ||
256 | handlers, | ||
257 | &ah_error_handler, | ||
258 | ah); | ||
259 | if (NULL == ah->mq) | ||
260 | { | ||
261 | GNUNET_break (0); | ||
262 | GNUNET_free (buf); | ||
263 | GNUNET_free (ah); | ||
264 | return NULL; | ||
265 | } | ||
266 | env = GNUNET_MQ_msg_extra (req, | ||
267 | size, | ||
268 | GNUNET_MESSAGE_TYPE_NAT_AUTO_REQUEST_CFG); | ||
269 | GNUNET_memcpy (&req[1], | ||
270 | buf, | ||
271 | size); | ||
272 | GNUNET_free (buf); | ||
273 | GNUNET_MQ_send (ah->mq, | ||
274 | env); | ||
275 | return ah; | ||
276 | } | ||
277 | |||
278 | |||
279 | /** | ||
280 | * Abort autoconfiguration. | ||
281 | * | ||
282 | * @param ah handle for operation to abort | ||
283 | */ | ||
284 | void | ||
285 | GNUNET_NAT_AUTO_autoconfig_cancel (struct GNUNET_NAT_AUTO_AutoHandle *ah) | ||
286 | { | ||
287 | GNUNET_MQ_destroy (ah->mq); | ||
288 | GNUNET_free (ah); | ||
289 | } | ||
290 | |||
291 | |||
292 | /* end of nat_api_auto.c */ | ||
diff --git a/src/service/nat-auto/nat_auto_api_test.c b/src/service/nat-auto/nat_auto_api_test.c new file mode 100644 index 000000000..9067aafda --- /dev/null +++ b/src/service/nat-auto/nat_auto_api_test.c | |||
@@ -0,0 +1,602 @@ | |||
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 | * @file nat/nat_auto_api_test.c | ||
22 | * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_nat_service.h" | ||
28 | #include "gnunet_nat_auto_service.h" | ||
29 | #include "nat-auto.h" | ||
30 | |||
31 | #define LOG(kind, ...) GNUNET_log_from (kind, "nat-auto", __VA_ARGS__) | ||
32 | |||
33 | #define NAT_SERVER_TIMEOUT \ | ||
34 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) | ||
35 | |||
36 | /** | ||
37 | * Entry we keep for each incoming connection. | ||
38 | */ | ||
39 | struct NatActivity | ||
40 | { | ||
41 | /** | ||
42 | * This is a doubly-linked list. | ||
43 | */ | ||
44 | struct NatActivity *next; | ||
45 | |||
46 | /** | ||
47 | * This is a doubly-linked list. | ||
48 | */ | ||
49 | struct NatActivity *prev; | ||
50 | |||
51 | /** | ||
52 | * Socket of the incoming connection. | ||
53 | */ | ||
54 | struct GNUNET_NETWORK_Handle *sock; | ||
55 | |||
56 | /** | ||
57 | * Handle of the master context. | ||
58 | */ | ||
59 | struct GNUNET_NAT_AUTO_Test *h; | ||
60 | |||
61 | /** | ||
62 | * Task reading from the incoming connection. | ||
63 | */ | ||
64 | struct GNUNET_SCHEDULER_Task *rtask; | ||
65 | }; | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Entry we keep for each connection to the gnunet-nat-service. | ||
70 | */ | ||
71 | struct ClientActivity | ||
72 | { | ||
73 | /** | ||
74 | * This is a doubly-linked list. | ||
75 | */ | ||
76 | struct ClientActivity *next; | ||
77 | |||
78 | /** | ||
79 | * This is a doubly-linked list. | ||
80 | */ | ||
81 | struct ClientActivity *prev; | ||
82 | |||
83 | /** | ||
84 | * Socket of the incoming connection. | ||
85 | */ | ||
86 | struct GNUNET_MQ_Handle *mq; | ||
87 | |||
88 | /** | ||
89 | * Handle to overall NAT test. | ||
90 | */ | ||
91 | struct GNUNET_NAT_AUTO_Test *h; | ||
92 | }; | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Handle to a NAT test. | ||
97 | */ | ||
98 | struct GNUNET_NAT_AUTO_Test | ||
99 | { | ||
100 | /** | ||
101 | * Configuration used | ||
102 | */ | ||
103 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
104 | |||
105 | /** | ||
106 | * Function to call with success report | ||
107 | */ | ||
108 | GNUNET_NAT_TestCallback report; | ||
109 | |||
110 | /** | ||
111 | * Closure for @e report. | ||
112 | */ | ||
113 | void *report_cls; | ||
114 | |||
115 | /** | ||
116 | * Handle to NAT traversal in use | ||
117 | */ | ||
118 | struct GNUNET_NAT_Handle *nat; | ||
119 | |||
120 | /** | ||
121 | * Handle to listen socket, or NULL | ||
122 | */ | ||
123 | struct GNUNET_NETWORK_Handle *lsock; | ||
124 | |||
125 | /** | ||
126 | * Head of list of nat activities. | ||
127 | */ | ||
128 | struct NatActivity *na_head; | ||
129 | |||
130 | /** | ||
131 | * Tail of list of nat activities. | ||
132 | */ | ||
133 | struct NatActivity *na_tail; | ||
134 | |||
135 | /** | ||
136 | * Head of list of client activities. | ||
137 | */ | ||
138 | struct ClientActivity *ca_head; | ||
139 | |||
140 | /** | ||
141 | * Tail of list of client activities. | ||
142 | */ | ||
143 | struct ClientActivity *ca_tail; | ||
144 | |||
145 | /** | ||
146 | * Identity of task for the listen socket (if any) | ||
147 | */ | ||
148 | struct GNUNET_SCHEDULER_Task *ltask; | ||
149 | |||
150 | /** | ||
151 | * Task identifier for the timeout (if any) | ||
152 | */ | ||
153 | struct GNUNET_SCHEDULER_Task *ttask; | ||
154 | |||
155 | /** | ||
156 | * Section name of plugin to test. | ||
157 | */ | ||
158 | char *section_name; | ||
159 | |||
160 | /** | ||
161 | * IPPROTO_TCP or IPPROTO_UDP. | ||
162 | */ | ||
163 | int proto; | ||
164 | |||
165 | /** | ||
166 | * Data that should be transmitted or source-port. | ||
167 | */ | ||
168 | uint16_t data; | ||
169 | |||
170 | /** | ||
171 | * Status code to be reported to the timeout/status call | ||
172 | */ | ||
173 | enum GNUNET_NAT_StatusCode status; | ||
174 | }; | ||
175 | |||
176 | |||
177 | /** | ||
178 | * Function called from #GNUNET_NAT_register whenever someone asks us | ||
179 | * to do connection reversal. | ||
180 | * | ||
181 | * @param cls closure, our `struct GNUNET_NAT_Handle` | ||
182 | * @param addr public IP address of the other peer | ||
183 | * @param addrlen actual length of the @a addr | ||
184 | */ | ||
185 | static void | ||
186 | reversal_cb (void *cls, const struct sockaddr *addr, socklen_t addrlen) | ||
187 | { | ||
188 | struct GNUNET_NAT_AUTO_Test *h = cls; | ||
189 | const struct sockaddr_in *sa; | ||
190 | |||
191 | if (sizeof(struct sockaddr_in) != addrlen) | ||
192 | return; | ||
193 | sa = (const struct sockaddr_in *) addr; | ||
194 | if (h->data != sa->sin_port) | ||
195 | { | ||
196 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
197 | "Received connection reversal request for wrong port\n"); | ||
198 | return; /* wrong port */ | ||
199 | } | ||
200 | /* report success */ | ||
201 | h->report (h->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
202 | } | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Activity on our incoming socket. Read data from the | ||
207 | * incoming connection. | ||
208 | * | ||
209 | * @param cls the `struct GNUNET_NAT_AUTO_Test` | ||
210 | */ | ||
211 | static void | ||
212 | do_udp_read (void *cls) | ||
213 | { | ||
214 | struct GNUNET_NAT_AUTO_Test *tst = cls; | ||
215 | uint16_t data; | ||
216 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
217 | |||
218 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
219 | tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
220 | tst->lsock, | ||
221 | &do_udp_read, | ||
222 | tst); | ||
223 | if ((NULL != tc->write_ready) && | ||
224 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, tst->lsock)) && | ||
225 | (sizeof(data) == | ||
226 | GNUNET_NETWORK_socket_recv (tst->lsock, &data, sizeof(data)))) | ||
227 | { | ||
228 | if (data == tst->data) | ||
229 | tst->report (tst->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
230 | else | ||
231 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
232 | "Received data mismatches expected value\n"); | ||
233 | } | ||
234 | else | ||
235 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
236 | "Failed to receive data from inbound connection\n"); | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Activity on our incoming socket. Read data from the | ||
242 | * incoming connection. | ||
243 | * | ||
244 | * @param cls the `struct NatActivity` | ||
245 | */ | ||
246 | static void | ||
247 | do_read (void *cls) | ||
248 | { | ||
249 | struct NatActivity *na = cls; | ||
250 | struct GNUNET_NAT_AUTO_Test *tst; | ||
251 | uint16_t data; | ||
252 | const struct GNUNET_SCHEDULER_TaskContext *tc; | ||
253 | |||
254 | tc = GNUNET_SCHEDULER_get_task_context (); | ||
255 | na->rtask = NULL; | ||
256 | tst = na->h; | ||
257 | GNUNET_CONTAINER_DLL_remove (tst->na_head, tst->na_tail, na); | ||
258 | if ((NULL != tc->write_ready) && | ||
259 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, na->sock)) && | ||
260 | (sizeof(data) == | ||
261 | GNUNET_NETWORK_socket_recv (na->sock, &data, sizeof(data)))) | ||
262 | { | ||
263 | if (data == tst->data) | ||
264 | tst->report (tst->report_cls, GNUNET_NAT_ERROR_SUCCESS); | ||
265 | else | ||
266 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
267 | "Received data does not match expected value\n"); | ||
268 | } | ||
269 | else | ||
270 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
271 | "Failed to receive data from inbound connection\n"); | ||
272 | GNUNET_NETWORK_socket_close (na->sock); | ||
273 | GNUNET_free (na); | ||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * Activity on our listen socket. Accept the | ||
279 | * incoming connection. | ||
280 | * | ||
281 | * @param cls the `struct GNUNET_NAT_AUTO_Test` | ||
282 | */ | ||
283 | static void | ||
284 | do_accept (void *cls) | ||
285 | { | ||
286 | struct GNUNET_NAT_AUTO_Test *tst = cls; | ||
287 | struct GNUNET_NETWORK_Handle *s; | ||
288 | struct NatActivity *wl; | ||
289 | |||
290 | tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
291 | tst->lsock, | ||
292 | &do_accept, | ||
293 | tst); | ||
294 | s = GNUNET_NETWORK_socket_accept (tst->lsock, NULL, NULL); | ||
295 | if (NULL == s) | ||
296 | { | ||
297 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept"); | ||
298 | return; /* odd error */ | ||
299 | } | ||
300 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
301 | "Got an inbound connection, waiting for data\n"); | ||
302 | wl = GNUNET_new (struct NatActivity); | ||
303 | wl->sock = s; | ||
304 | wl->h = tst; | ||
305 | wl->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
306 | wl->sock, | ||
307 | &do_read, | ||
308 | wl); | ||
309 | GNUNET_CONTAINER_DLL_insert (tst->na_head, tst->na_tail, wl); | ||
310 | } | ||
311 | |||
312 | |||
313 | /** | ||
314 | * We got disconnected from the NAT server. Stop | ||
315 | * waiting for a reply. | ||
316 | * | ||
317 | * @param cls the `struct ClientActivity` | ||
318 | * @param error error code | ||
319 | */ | ||
320 | static void | ||
321 | mq_error_handler (void *cls, enum GNUNET_MQ_Error error) | ||
322 | { | ||
323 | struct ClientActivity *ca = cls; | ||
324 | struct GNUNET_NAT_AUTO_Test *tst = ca->h; | ||
325 | |||
326 | GNUNET_CONTAINER_DLL_remove (tst->ca_head, tst->ca_tail, ca); | ||
327 | GNUNET_MQ_destroy (ca->mq); | ||
328 | GNUNET_free (ca); | ||
329 | } | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Address-callback, used to send message to gnunet-nat-server. | ||
334 | * | ||
335 | * @param cls closure | ||
336 | * @param[in,out] app_ctx location where the app can store stuff | ||
337 | * on add and retrieve it on remove | ||
338 | * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean | ||
339 | * the previous (now invalid) one | ||
340 | * @param ac address class the address belongs to | ||
341 | * @param addr either the previous or the new public IP address | ||
342 | * @param addrlen actual length of the @a addr | ||
343 | */ | ||
344 | static void | ||
345 | addr_cb (void *cls, | ||
346 | void **app_ctx, | ||
347 | int add_remove, | ||
348 | enum GNUNET_NAT_AddressClass ac, | ||
349 | const struct sockaddr *addr, | ||
350 | socklen_t addrlen) | ||
351 | { | ||
352 | struct GNUNET_NAT_AUTO_Test *h = cls; | ||
353 | struct ClientActivity *ca; | ||
354 | struct GNUNET_MQ_Envelope *env; | ||
355 | struct GNUNET_NAT_AUTO_TestMessage *msg; | ||
356 | const struct sockaddr_in *sa; | ||
357 | |||
358 | (void) app_ctx; | ||
359 | if (GNUNET_YES != add_remove) | ||
360 | return; | ||
361 | if (addrlen != sizeof(struct sockaddr_in)) | ||
362 | { | ||
363 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
364 | "NAT test ignores IPv6 address `%s' returned from NAT library\n", | ||
365 | GNUNET_a2s (addr, addrlen)); | ||
366 | return; /* ignore IPv6 here */ | ||
367 | } | ||
368 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
369 | "Asking gnunet-nat-server to connect to `%s'\n", | ||
370 | GNUNET_a2s (addr, addrlen)); | ||
371 | |||
372 | ca = GNUNET_new (struct ClientActivity); | ||
373 | ca->h = h; | ||
374 | ca->mq = GNUNET_CLIENT_connect (h->cfg, | ||
375 | "gnunet-nat-server", | ||
376 | NULL, | ||
377 | &mq_error_handler, | ||
378 | ca); | ||
379 | if (NULL == ca->mq) | ||
380 | { | ||
381 | GNUNET_free (ca); | ||
382 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
383 | _ ("Failed to connect to `gnunet-nat-server'\n")); | ||
384 | return; | ||
385 | } | ||
386 | GNUNET_CONTAINER_DLL_insert (h->ca_head, h->ca_tail, ca); | ||
387 | sa = (const struct sockaddr_in *) addr; | ||
388 | env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAT_TEST); | ||
389 | msg->dst_ipv4 = sa->sin_addr.s_addr; | ||
390 | msg->dport = sa->sin_port; | ||
391 | msg->data = h->data; | ||
392 | msg->is_tcp = htonl ((uint32_t) (h->proto == IPPROTO_TCP)); | ||
393 | GNUNET_MQ_send (ca->mq, env); | ||
394 | } | ||
395 | |||
396 | |||
397 | /** | ||
398 | * Calls the report-callback reporting failure. | ||
399 | * | ||
400 | * Destroys the nat handle after the callback has been processed. | ||
401 | * | ||
402 | * @param cls handle to the timed out NAT test | ||
403 | */ | ||
404 | static void | ||
405 | do_fail (void *cls) | ||
406 | { | ||
407 | struct GNUNET_NAT_AUTO_Test *nh = cls; | ||
408 | |||
409 | nh->ttask = NULL; | ||
410 | nh->report (nh->report_cls, nh->status); | ||
411 | } | ||
412 | |||
413 | |||
414 | /** | ||
415 | * Start testing if NAT traversal works using the given configuration. | ||
416 | * The transport adapters should be down while using this function. | ||
417 | * | ||
418 | * @param cfg configuration for the NAT traversal | ||
419 | * @param proto protocol to test, i.e. IPPROTO_TCP or IPPROTO_UDP | ||
420 | * @param section_name configuration section to use for configuration | ||
421 | * @param report function to call with the result of the test | ||
422 | * @param report_cls closure for @a report | ||
423 | * @return handle to cancel NAT test | ||
424 | */ | ||
425 | struct GNUNET_NAT_AUTO_Test * | ||
426 | GNUNET_NAT_AUTO_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
427 | uint8_t proto, | ||
428 | const char *section_name, | ||
429 | GNUNET_NAT_TestCallback report, | ||
430 | void *report_cls) | ||
431 | { | ||
432 | struct GNUNET_NAT_AUTO_Test *nh; | ||
433 | unsigned long long bnd_port; | ||
434 | struct sockaddr_in sa; | ||
435 | const struct sockaddr *addrs[] = { (const struct sockaddr *) &sa }; | ||
436 | const socklen_t addrlens[] = { sizeof(sa) }; | ||
437 | |||
438 | if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, | ||
439 | section_name, | ||
440 | "PORT", | ||
441 | &bnd_port)) || | ||
442 | (bnd_port > 65535)) | ||
443 | { | ||
444 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
445 | _ ("Failed to find valid PORT in section `%s'\n"), | ||
446 | section_name); | ||
447 | return NULL; | ||
448 | } | ||
449 | |||
450 | memset (&sa, 0, sizeof(sa)); | ||
451 | sa.sin_family = AF_INET; | ||
452 | sa.sin_port = htons ((uint16_t) bnd_port); | ||
453 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
454 | sa.sin_len = sizeof(sa); | ||
455 | #endif | ||
456 | |||
457 | nh = GNUNET_new (struct GNUNET_NAT_AUTO_Test); | ||
458 | nh->cfg = cfg; | ||
459 | nh->proto = proto; | ||
460 | nh->section_name = GNUNET_strdup (section_name); | ||
461 | nh->report = report; | ||
462 | nh->report_cls = report_cls; | ||
463 | nh->status = GNUNET_NAT_ERROR_SUCCESS; | ||
464 | if (0 == bnd_port) | ||
465 | { | ||
466 | nh->nat = GNUNET_NAT_register (cfg, | ||
467 | section_name, | ||
468 | proto, | ||
469 | 0, | ||
470 | NULL, | ||
471 | NULL, | ||
472 | &addr_cb, | ||
473 | &reversal_cb, | ||
474 | nh); | ||
475 | } | ||
476 | else | ||
477 | { | ||
478 | nh->lsock = | ||
479 | GNUNET_NETWORK_socket_create (AF_INET, | ||
480 | (IPPROTO_UDP == proto) ? SOCK_DGRAM | ||
481 | : SOCK_STREAM, | ||
482 | proto); | ||
483 | if ((NULL == nh->lsock) || | ||
484 | (GNUNET_OK != GNUNET_NETWORK_socket_bind (nh->lsock, | ||
485 | (const struct sockaddr *) &sa, | ||
486 | sizeof(sa)))) | ||
487 | { | ||
488 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
489 | _ ("Failed to create socket bound to `%s' for NAT test: %s\n"), | ||
490 | GNUNET_a2s ((const struct sockaddr *) &sa, sizeof(sa)), | ||
491 | strerror (errno)); | ||
492 | if (NULL != nh->lsock) | ||
493 | { | ||
494 | GNUNET_NETWORK_socket_close (nh->lsock); | ||
495 | nh->lsock = NULL; | ||
496 | } | ||
497 | nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR; | ||
498 | nh->ttask = GNUNET_SCHEDULER_add_now (&do_fail, nh); | ||
499 | return nh; | ||
500 | } | ||
501 | if (IPPROTO_TCP == proto) | ||
502 | { | ||
503 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_listen (nh->lsock, 5)); | ||
504 | nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
505 | nh->lsock, | ||
506 | &do_accept, | ||
507 | nh); | ||
508 | } | ||
509 | else | ||
510 | { | ||
511 | nh->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
512 | nh->lsock, | ||
513 | &do_udp_read, | ||
514 | nh); | ||
515 | } | ||
516 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
517 | "NAT test listens on port %llu (%s)\n", | ||
518 | bnd_port, | ||
519 | (IPPROTO_TCP == proto) ? "tcp" : "udp"); | ||
520 | nh->nat = GNUNET_NAT_register (cfg, | ||
521 | section_name, | ||
522 | proto, | ||
523 | 1, | ||
524 | addrs, | ||
525 | addrlens, | ||
526 | &addr_cb, | ||
527 | NULL, | ||
528 | nh); | ||
529 | if (NULL == nh->nat) | ||
530 | { | ||
531 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
532 | _ ("NAT test failed to start NAT library\n")); | ||
533 | if (NULL != nh->ltask) | ||
534 | { | ||
535 | GNUNET_SCHEDULER_cancel (nh->ltask); | ||
536 | nh->ltask = NULL; | ||
537 | } | ||
538 | if (NULL != nh->lsock) | ||
539 | { | ||
540 | GNUNET_NETWORK_socket_close (nh->lsock); | ||
541 | nh->lsock = NULL; | ||
542 | } | ||
543 | nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED; | ||
544 | nh->ttask = GNUNET_SCHEDULER_add_now (&do_fail, nh); | ||
545 | return nh; | ||
546 | } | ||
547 | } | ||
548 | return nh; | ||
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * Stop an active NAT test. | ||
554 | * | ||
555 | * @param tst test to stop. | ||
556 | */ | ||
557 | void | ||
558 | GNUNET_NAT_AUTO_test_stop (struct GNUNET_NAT_AUTO_Test *tst) | ||
559 | { | ||
560 | struct NatActivity *pos; | ||
561 | struct ClientActivity *cpos; | ||
562 | |||
563 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping NAT test\n"); | ||
564 | while (NULL != (cpos = tst->ca_head)) | ||
565 | { | ||
566 | GNUNET_CONTAINER_DLL_remove (tst->ca_head, tst->ca_tail, cpos); | ||
567 | GNUNET_MQ_destroy (cpos->mq); | ||
568 | GNUNET_free (cpos); | ||
569 | } | ||
570 | while (NULL != (pos = tst->na_head)) | ||
571 | { | ||
572 | GNUNET_CONTAINER_DLL_remove (tst->na_head, tst->na_tail, pos); | ||
573 | GNUNET_SCHEDULER_cancel (pos->rtask); | ||
574 | GNUNET_NETWORK_socket_close (pos->sock); | ||
575 | GNUNET_free (pos); | ||
576 | } | ||
577 | if (NULL != tst->ttask) | ||
578 | { | ||
579 | GNUNET_SCHEDULER_cancel (tst->ttask); | ||
580 | tst->ttask = NULL; | ||
581 | } | ||
582 | if (NULL != tst->ltask) | ||
583 | { | ||
584 | GNUNET_SCHEDULER_cancel (tst->ltask); | ||
585 | tst->ltask = NULL; | ||
586 | } | ||
587 | if (NULL != tst->lsock) | ||
588 | { | ||
589 | GNUNET_NETWORK_socket_close (tst->lsock); | ||
590 | tst->lsock = NULL; | ||
591 | } | ||
592 | if (NULL != tst->nat) | ||
593 | { | ||
594 | GNUNET_NAT_unregister (tst->nat); | ||
595 | tst->nat = NULL; | ||
596 | } | ||
597 | GNUNET_free (tst->section_name); | ||
598 | GNUNET_free (tst); | ||
599 | } | ||
600 | |||
601 | |||
602 | /* end of nat_auto_api_test.c */ | ||