make sure nat-auto code uses NAT_AUTO prefix
[oweals/gnunet.git] / src / nat-auto / nat_auto_api_test.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
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
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_lib.h"
28 #include "nat-auto.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
31
32 #define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
33
34 /**
35  * Entry we keep for each incoming connection.
36  */
37 struct NatActivity
38 {
39   /**
40    * This is a doubly-linked list.
41    */
42   struct NatActivity *next;
43
44   /**
45    * This is a doubly-linked list.
46    */
47   struct NatActivity *prev;
48
49   /**
50    * Socket of the incoming connection.
51    */
52   struct GNUNET_NETWORK_Handle *sock;
53
54   /**
55    * Handle of the master context.
56    */
57   struct GNUNET_NAT_AUTO_Test *h;
58
59   /**
60    * Task reading from the incoming connection.
61    */
62   struct GNUNET_SCHEDULER_Task *rtask;
63 };
64
65
66 /**
67  * Entry we keep for each connection to the gnunet-nat-service.
68  */
69 struct ClientActivity
70 {
71   /**
72    * This is a doubly-linked list.
73    */
74   struct ClientActivity *next;
75
76   /**
77    * This is a doubly-linked list.
78    */
79   struct ClientActivity *prev;
80
81   /**
82    * Socket of the incoming connection.
83    */
84   struct GNUNET_MQ_Handle *mq;
85
86   /**
87    * Handle to overall NAT test.
88    */
89   struct GNUNET_NAT_AUTO_Test *h;
90
91 };
92
93
94 /**
95  * Handle to a NAT test.
96  */
97 struct GNUNET_NAT_AUTO_Test
98 {
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,
187              const struct sockaddr *addr,
188              socklen_t addrlen)
189 {
190   struct GNUNET_NAT_AUTO_Test *h = cls;
191   const struct sockaddr_in *sa;
192
193   if (sizeof (struct sockaddr_in) != addrlen)
194     return;
195   sa = (const struct sockaddr_in *) addr;
196   if (h->data != sa->sin_port)
197   {
198     LOG (GNUNET_ERROR_TYPE_DEBUG,
199          "Received connection reversal request for wrong port\n");
200     return;                     /* wrong port */
201   }
202   /* report success */
203   h->report (h->report_cls,
204              GNUNET_NAT_ERROR_SUCCESS);
205 }
206
207
208 /**
209  * Activity on our incoming socket.  Read data from the
210  * incoming connection.
211  *
212  * @param cls the `struct GNUNET_NAT_AUTO_Test`
213  */
214 static void
215 do_udp_read (void *cls)
216 {
217   struct GNUNET_NAT_AUTO_Test *tst = cls;
218   uint16_t data;
219   const struct GNUNET_SCHEDULER_TaskContext *tc;
220
221   tc = GNUNET_SCHEDULER_get_task_context ();
222   tst->ltask =
223       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
224                                      tst->lsock,
225                                      &do_udp_read,
226                                      tst);
227   if ((NULL != tc->write_ready) &&
228       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
229                                    tst->lsock)) &&
230       (sizeof (data) ==
231        GNUNET_NETWORK_socket_recv (tst->lsock,
232                                    &data,
233                                    sizeof (data))))
234   {
235     if (data == tst->data)
236       tst->report (tst->report_cls,
237                    GNUNET_NAT_ERROR_SUCCESS);
238     else
239       LOG (GNUNET_ERROR_TYPE_DEBUG,
240            "Received data mismatches expected value\n");
241   }
242   else
243     LOG (GNUNET_ERROR_TYPE_DEBUG,
244          "Failed to receive data from inbound connection\n");
245 }
246
247
248 /**
249  * Activity on our incoming socket.  Read data from the
250  * incoming connection.
251  *
252  * @param cls the `struct NatActivity`
253  */
254 static void
255 do_read (void *cls)
256 {
257   struct NatActivity *na = cls;
258   struct GNUNET_NAT_AUTO_Test *tst;
259   uint16_t data;
260   const struct GNUNET_SCHEDULER_TaskContext *tc;
261
262   tc = GNUNET_SCHEDULER_get_task_context ();
263   na->rtask = NULL;
264   tst = na->h;
265   GNUNET_CONTAINER_DLL_remove (tst->na_head,
266                                tst->na_tail,
267                                na);
268   if ((NULL != tc->write_ready) &&
269       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
270                                    na->sock)) &&
271       (sizeof (data) ==
272        GNUNET_NETWORK_socket_recv (na->sock,
273                                    &data,
274                                    sizeof (data))))
275   {
276     if (data == tst->data)
277       tst->report (tst->report_cls,
278                    GNUNET_NAT_ERROR_SUCCESS);
279     else
280       LOG (GNUNET_ERROR_TYPE_DEBUG,
281            "Received data does not match expected value\n");
282   }
283   else
284     LOG (GNUNET_ERROR_TYPE_DEBUG,
285          "Failed to receive data from inbound connection\n");
286   GNUNET_NETWORK_socket_close (na->sock);
287   GNUNET_free (na);
288 }
289
290
291 /**
292  * Activity on our listen socket. Accept the
293  * incoming connection.
294  *
295  * @param cls the `struct GNUNET_NAT_AUTO_Test`
296  */
297 static void
298 do_accept (void *cls)
299 {
300   struct GNUNET_NAT_AUTO_Test *tst = cls;
301   struct GNUNET_NETWORK_Handle *s;
302   struct NatActivity *wl;
303
304   tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
305                                               tst->lsock,
306                                               &do_accept,
307                                               tst);
308   s = GNUNET_NETWORK_socket_accept (tst->lsock,
309                                     NULL,
310                                     NULL);
311   if (NULL == s)
312   {
313     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
314                          "accept");
315     return;                     /* odd error */
316   }
317   LOG (GNUNET_ERROR_TYPE_DEBUG,
318        "Got an inbound connection, waiting for data\n");
319   wl = GNUNET_new (struct NatActivity);
320   wl->sock = s;
321   wl->h = tst;
322   wl->rtask =
323     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
324                                    wl->sock,
325                                    &do_read,
326                                    wl);
327   GNUNET_CONTAINER_DLL_insert (tst->na_head,
328                                tst->na_tail,
329                                wl);
330 }
331
332
333 /**
334  * We got disconnected from the NAT server.  Stop
335  * waiting for a reply.
336  *
337  * @param cls the `struct ClientActivity`
338  * @param error error code
339  */
340 static void
341 mq_error_handler (void *cls,
342                   enum GNUNET_MQ_Error error)
343 {
344   struct ClientActivity *ca = cls;
345   struct GNUNET_NAT_AUTO_Test *tst = ca->h;
346
347   GNUNET_CONTAINER_DLL_remove (tst->ca_head,
348                                tst->ca_tail,
349                                ca);
350   GNUNET_MQ_destroy (ca->mq);
351   GNUNET_free (ca);
352 }
353
354
355 /**
356  * Address-callback, used to send message to gnunet-nat-server.
357  *
358  * @param cls closure
359  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
360  *     the previous (now invalid) one
361  * @param addr either the previous or the new public IP address
362  * @param addrlen actual length of the @a addr
363  */
364 static void
365 addr_cb (void *cls,
366          int add_remove,
367          const struct sockaddr *addr,
368          socklen_t addrlen)
369 {
370   struct GNUNET_NAT_AUTO_Test *h = cls;
371   struct ClientActivity *ca;
372   struct GNUNET_MQ_Envelope *env;
373   struct GNUNET_NAT_AUTO_TestMessage *msg;
374   const struct sockaddr_in *sa;
375
376   if (GNUNET_YES != add_remove)
377     return;
378   if (addrlen != sizeof (struct sockaddr_in))
379   {
380     LOG (GNUNET_ERROR_TYPE_DEBUG,
381          "NAT test ignores IPv6 address `%s' returned from NAT library\n",
382          GNUNET_a2s (addr,
383                      addrlen));
384     return;                     /* ignore IPv6 here */
385   }
386   LOG (GNUNET_ERROR_TYPE_INFO,
387        "Asking gnunet-nat-server to connect to `%s'\n",
388        GNUNET_a2s (addr,
389                    addrlen));
390
391   ca = GNUNET_new (struct ClientActivity);
392   ca->h = h;
393   ca->mq = GNUNET_CLIENT_connecT (h->cfg,
394                                   "gnunet-nat-server",
395                                   NULL,
396                                   &mq_error_handler,
397                                   ca);
398   if (NULL == ca->mq)
399   {
400     GNUNET_free (ca);
401     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
402                 _("Failed to connect to `gnunet-nat-server'\n"));
403     return;
404   }
405   GNUNET_CONTAINER_DLL_insert (h->ca_head,
406                                h->ca_tail,
407                                ca);
408   sa = (const struct sockaddr_in *) addr;
409   env = GNUNET_MQ_msg (msg,
410                        GNUNET_MESSAGE_TYPE_NAT_TEST);
411   msg->dst_ipv4 = sa->sin_addr.s_addr;
412   msg->dport = sa->sin_port;
413   msg->data = h->data;
414   msg->is_tcp = htonl ((uint32_t) h->is_tcp);
415   GNUNET_MQ_send (ca->mq,
416                   env);
417 }
418
419
420 /**
421  * Timeout task for a nat test.
422  * Calls the report-callback with a timeout return value
423  *
424  * Destroys the nat handle after the callback has been processed.
425  *
426  * @param cls handle to the timed out NAT test
427  */
428 static void
429 do_timeout (void *cls)
430 {
431   struct GNUNET_NAT_AUTO_Test *nh = cls;
432
433   nh->ttask = NULL;
434   nh->report (nh->report_cls,
435               (GNUNET_NAT_ERROR_SUCCESS == nh->status)
436               ? GNUNET_NAT_ERROR_TIMEOUT
437               : nh->status);
438 }
439
440
441 /**
442  * Start testing if NAT traversal works using the
443  * given configuration (IPv4-only).
444  *
445  * ALL failures are reported directly to the report callback
446  *
447  * @param cfg configuration for the NAT traversal
448  * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP
449  * @param bnd_port port to bind to, 0 for connection reversal
450  * @param adv_port externally advertised port to use
451  * @param timeout delay after which the test should be aborted
452  * @param report function to call with the result of the test
453  * @param report_cls closure for @a report
454  * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback
455  */
456 struct GNUNET_NAT_AUTO_Test *
457 GNUNET_NAT_AUTO_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
458                        int is_tcp,
459                        uint16_t bnd_port,
460                        uint16_t adv_port,
461                        struct GNUNET_TIME_Relative timeout,
462                        GNUNET_NAT_TestCallback report,
463                        void *report_cls)
464 {
465   struct GNUNET_NAT_AUTO_Test *nh;
466   struct sockaddr_in sa;
467   const struct sockaddr *addrs[] = {
468     (const struct sockaddr *) &sa
469   };
470   const socklen_t addrlens[] = {
471     sizeof (sa)
472   };
473
474   memset (&sa, 0, sizeof (sa));
475   sa.sin_family = AF_INET;
476   sa.sin_port = htons (bnd_port);
477 #if HAVE_SOCKADDR_IN_SIN_LEN
478   sa.sin_len = sizeof (sa);
479 #endif
480
481   nh = GNUNET_new (struct GNUNET_NAT_AUTO_Test);
482   nh->cfg = cfg;
483   nh->is_tcp = is_tcp;
484   nh->data = bnd_port;
485   nh->adv_port = adv_port;
486   nh->report = report;
487   nh->report_cls = report_cls;
488   nh->status = GNUNET_NAT_ERROR_SUCCESS;
489   if (0 == bnd_port)
490   {
491     nh->nat
492       = GNUNET_NAT_register (cfg,
493                              is_tcp,
494                              0,
495                              0,
496                              NULL,
497                              NULL,
498                              &addr_cb,
499                              &reversal_cb,
500                              nh,
501                              NULL);
502   }
503   else
504   {
505     nh->lsock =
506         GNUNET_NETWORK_socket_create (AF_INET,
507                                       (is_tcp ==
508                                        GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM,
509                                       0);
510     if ((nh->lsock == NULL) ||
511         (GNUNET_OK !=
512          GNUNET_NETWORK_socket_bind (nh->lsock,
513                                      (const struct sockaddr *) &sa,
514                                      sizeof (sa))))
515     {
516       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
517                   _("Failed to create listen socket bound to `%s' for NAT test: %s\n"),
518                   GNUNET_a2s ((const struct sockaddr *) &sa,
519                               sizeof (sa)),
520                   STRERROR (errno));
521       if (NULL != nh->lsock)
522       {
523         GNUNET_NETWORK_socket_close (nh->lsock);
524         nh->lsock = NULL;
525       }
526       nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR;
527       nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
528                                             nh);
529       return nh;
530     }
531     if (GNUNET_YES == is_tcp)
532     {
533       GNUNET_break (GNUNET_OK ==
534                     GNUNET_NETWORK_socket_listen (nh->lsock,
535                                                   5));
536       nh->ltask =
537           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
538                                          nh->lsock,
539                                          &do_accept,
540                                          nh);
541     }
542     else
543     {
544       nh->ltask =
545           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
546                                          nh->lsock,
547                                          &do_udp_read,
548                                          nh);
549     }
550     LOG (GNUNET_ERROR_TYPE_INFO,
551          "NAT test listens on port %u (%s)\n",
552          bnd_port,
553          (GNUNET_YES == is_tcp) ? "tcp" : "udp");
554     nh->nat = GNUNET_NAT_register (cfg,
555                                    is_tcp,
556                                    adv_port,
557                                    1,
558                                    addrs,
559                                    addrlens,
560                                    &addr_cb,
561                                    NULL,
562                                    nh,
563                                    NULL);
564     if (NULL == nh->nat)
565     {
566       LOG (GNUNET_ERROR_TYPE_INFO,
567           _("NAT test failed to start NAT library\n"));
568       if (NULL != nh->ltask)
569       {
570         GNUNET_SCHEDULER_cancel (nh->ltask);
571         nh->ltask = NULL;
572       }
573       if (NULL != nh->lsock)
574       {
575         GNUNET_NETWORK_socket_close (nh->lsock);
576         nh->lsock = NULL;
577       }
578       nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED;
579       nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
580                                             nh);
581       return nh;
582     }
583   }
584   nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout,
585                                             &do_timeout,
586                                             nh);
587   return nh;
588 }
589
590
591 /**
592  * Stop an active NAT test.
593  *
594  * @param tst test to stop.
595  */
596 void
597 GNUNET_NAT_AUTO_test_stop (struct GNUNET_NAT_AUTO_Test *tst)
598 {
599   struct NatActivity *pos;
600   struct ClientActivity *cpos;
601
602   LOG (GNUNET_ERROR_TYPE_DEBUG,
603        "Stopping NAT test\n");
604   while (NULL != (cpos = tst->ca_head))
605   {
606     GNUNET_CONTAINER_DLL_remove (tst->ca_head,
607                                  tst->ca_tail,
608                                  cpos);
609     GNUNET_MQ_destroy (cpos->mq);
610     GNUNET_free (cpos);
611   }
612   while (NULL != (pos = tst->na_head))
613   {
614     GNUNET_CONTAINER_DLL_remove (tst->na_head,
615                                  tst->na_tail,
616                                  pos);
617     GNUNET_SCHEDULER_cancel (pos->rtask);
618     GNUNET_NETWORK_socket_close (pos->sock);
619     GNUNET_free (pos);
620   }
621   if (NULL != tst->ttask)
622   {
623     GNUNET_SCHEDULER_cancel (tst->ttask);
624     tst->ttask = NULL;
625   }
626   if (NULL != tst->ltask)
627   {
628     GNUNET_SCHEDULER_cancel (tst->ltask);
629     tst->ltask = NULL;
630   }
631   if (NULL != tst->lsock)
632   {
633     GNUNET_NETWORK_socket_close (tst->lsock);
634     tst->lsock = NULL;
635   }
636   if (NULL != tst->nat)
637   {
638     GNUNET_NAT_unregister (tst->nat);
639     tst->nat = NULL;
640   }
641   GNUNET_free (tst);
642 }
643
644 /* end of nat_auto_api_test.c */