error handling
[oweals/gnunet.git] / src / nat-auto / gnunet-nat-auto_legacy.c
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 lenght 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 */