glitch in the license text detected by hyazinthe, thank you!
[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 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 /**
16  * @file nat/nat_auto_api_test.c
17  * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server)
18  * @author Christian Grothoff
19  */
20 #include "platform.h"
21 #include "gnunet_util_lib.h"
22 #include "gnunet_nat_service.h"
23 #include "gnunet_nat_auto_service.h"
24 #include "nat-auto.h"
25
26 #define LOG(kind,...) GNUNET_log_from (kind, "nat-auto", __VA_ARGS__)
27
28 #define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
29
30 /**
31  * Entry we keep for each incoming connection.
32  */
33 struct NatActivity
34 {
35   /**
36    * This is a doubly-linked list.
37    */
38   struct NatActivity *next;
39
40   /**
41    * This is a doubly-linked list.
42    */
43   struct NatActivity *prev;
44
45   /**
46    * Socket of the incoming connection.
47    */
48   struct GNUNET_NETWORK_Handle *sock;
49
50   /**
51    * Handle of the master context.
52    */
53   struct GNUNET_NAT_AUTO_Test *h;
54
55   /**
56    * Task reading from the incoming connection.
57    */
58   struct GNUNET_SCHEDULER_Task *rtask;
59 };
60
61
62 /**
63  * Entry we keep for each connection to the gnunet-nat-service.
64  */
65 struct ClientActivity
66 {
67   /**
68    * This is a doubly-linked list.
69    */
70   struct ClientActivity *next;
71
72   /**
73    * This is a doubly-linked list.
74    */
75   struct ClientActivity *prev;
76
77   /**
78    * Socket of the incoming connection.
79    */
80   struct GNUNET_MQ_Handle *mq;
81
82   /**
83    * Handle to overall NAT test.
84    */
85   struct GNUNET_NAT_AUTO_Test *h;
86
87 };
88
89
90 /**
91  * Handle to a NAT test.
92  */
93 struct GNUNET_NAT_AUTO_Test
94 {
95
96   /**
97    * Configuration used
98    */
99   const struct GNUNET_CONFIGURATION_Handle *cfg;
100
101   /**
102    * Function to call with success report
103    */
104   GNUNET_NAT_TestCallback report;
105
106   /**
107    * Closure for @e report.
108    */
109   void *report_cls;
110
111   /**
112    * Handle to NAT traversal in use
113    */
114   struct GNUNET_NAT_Handle *nat;
115
116   /**
117    * Handle to listen socket, or NULL
118    */
119   struct GNUNET_NETWORK_Handle *lsock;
120
121   /**
122    * Head of list of nat activities.
123    */
124   struct NatActivity *na_head;
125
126   /**
127    * Tail of list of nat activities.
128    */
129   struct NatActivity *na_tail;
130
131   /**
132    * Head of list of client activities.
133    */
134   struct ClientActivity *ca_head;
135
136   /**
137    * Tail of list of client activities.
138    */
139   struct ClientActivity *ca_tail;
140
141   /**
142    * Identity of task for the listen socket (if any)
143    */
144   struct GNUNET_SCHEDULER_Task *ltask;
145
146   /**
147    * Task identifier for the timeout (if any)
148    */
149   struct GNUNET_SCHEDULER_Task *ttask;
150
151   /**
152    * Section name of plugin to test.
153    */
154   char *section_name;
155
156   /**
157    * IPPROTO_TCP or IPPROTO_UDP.
158    */
159   int proto;
160
161   /**
162    * Data that should be transmitted or source-port.
163    */
164   uint16_t data;
165
166   /**
167    * Status code to be reported to the timeout/status call
168    */
169   enum GNUNET_NAT_StatusCode status;
170 };
171
172
173 /**
174  * Function called from #GNUNET_NAT_register whenever someone asks us
175  * to do connection reversal.
176  *
177  * @param cls closure, our `struct GNUNET_NAT_Handle`
178  * @param addr public IP address of the other peer
179  * @param addrlen actual lenght of the @a addr
180  */
181 static void
182 reversal_cb (void *cls,
183              const struct sockaddr *addr,
184              socklen_t addrlen)
185 {
186   struct GNUNET_NAT_AUTO_Test *h = cls;
187   const struct sockaddr_in *sa;
188
189   if (sizeof (struct sockaddr_in) != addrlen)
190     return;
191   sa = (const struct sockaddr_in *) addr;
192   if (h->data != sa->sin_port)
193   {
194     LOG (GNUNET_ERROR_TYPE_DEBUG,
195          "Received connection reversal request for wrong port\n");
196     return;                     /* wrong port */
197   }
198   /* report success */
199   h->report (h->report_cls,
200              GNUNET_NAT_ERROR_SUCCESS);
201 }
202
203
204 /**
205  * Activity on our incoming socket.  Read data from the
206  * incoming connection.
207  *
208  * @param cls the `struct GNUNET_NAT_AUTO_Test`
209  */
210 static void
211 do_udp_read (void *cls)
212 {
213   struct GNUNET_NAT_AUTO_Test *tst = cls;
214   uint16_t data;
215   const struct GNUNET_SCHEDULER_TaskContext *tc;
216
217   tc = GNUNET_SCHEDULER_get_task_context ();
218   tst->ltask =
219       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,
225                                    tst->lsock)) &&
226       (sizeof (data) ==
227        GNUNET_NETWORK_socket_recv (tst->lsock,
228                                    &data,
229                                    sizeof (data))))
230   {
231     if (data == tst->data)
232       tst->report (tst->report_cls,
233                    GNUNET_NAT_ERROR_SUCCESS);
234     else
235       LOG (GNUNET_ERROR_TYPE_DEBUG,
236            "Received data mismatches expected value\n");
237   }
238   else
239     LOG (GNUNET_ERROR_TYPE_DEBUG,
240          "Failed to receive data from inbound connection\n");
241 }
242
243
244 /**
245  * Activity on our incoming socket.  Read data from the
246  * incoming connection.
247  *
248  * @param cls the `struct NatActivity`
249  */
250 static void
251 do_read (void *cls)
252 {
253   struct NatActivity *na = cls;
254   struct GNUNET_NAT_AUTO_Test *tst;
255   uint16_t data;
256   const struct GNUNET_SCHEDULER_TaskContext *tc;
257
258   tc = GNUNET_SCHEDULER_get_task_context ();
259   na->rtask = NULL;
260   tst = na->h;
261   GNUNET_CONTAINER_DLL_remove (tst->na_head,
262                                tst->na_tail,
263                                na);
264   if ((NULL != tc->write_ready) &&
265       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
266                                    na->sock)) &&
267       (sizeof (data) ==
268        GNUNET_NETWORK_socket_recv (na->sock,
269                                    &data,
270                                    sizeof (data))))
271   {
272     if (data == tst->data)
273       tst->report (tst->report_cls,
274                    GNUNET_NAT_ERROR_SUCCESS);
275     else
276       LOG (GNUNET_ERROR_TYPE_DEBUG,
277            "Received data does not match expected value\n");
278   }
279   else
280     LOG (GNUNET_ERROR_TYPE_DEBUG,
281          "Failed to receive data from inbound connection\n");
282   GNUNET_NETWORK_socket_close (na->sock);
283   GNUNET_free (na);
284 }
285
286
287 /**
288  * Activity on our listen socket. Accept the
289  * incoming connection.
290  *
291  * @param cls the `struct GNUNET_NAT_AUTO_Test`
292  */
293 static void
294 do_accept (void *cls)
295 {
296   struct GNUNET_NAT_AUTO_Test *tst = cls;
297   struct GNUNET_NETWORK_Handle *s;
298   struct NatActivity *wl;
299
300   tst->ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
301                                               tst->lsock,
302                                               &do_accept,
303                                               tst);
304   s = GNUNET_NETWORK_socket_accept (tst->lsock,
305                                     NULL,
306                                     NULL);
307   if (NULL == s)
308   {
309     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
310                          "accept");
311     return;                     /* odd error */
312   }
313   LOG (GNUNET_ERROR_TYPE_DEBUG,
314        "Got an inbound connection, waiting for data\n");
315   wl = GNUNET_new (struct NatActivity);
316   wl->sock = s;
317   wl->h = tst;
318   wl->rtask =
319     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
320                                    wl->sock,
321                                    &do_read,
322                                    wl);
323   GNUNET_CONTAINER_DLL_insert (tst->na_head,
324                                tst->na_tail,
325                                wl);
326 }
327
328
329 /**
330  * We got disconnected from the NAT server.  Stop
331  * waiting for a reply.
332  *
333  * @param cls the `struct ClientActivity`
334  * @param error error code
335  */
336 static void
337 mq_error_handler (void *cls,
338                   enum GNUNET_MQ_Error error)
339 {
340   struct ClientActivity *ca = cls;
341   struct GNUNET_NAT_AUTO_Test *tst = ca->h;
342
343   GNUNET_CONTAINER_DLL_remove (tst->ca_head,
344                                tst->ca_tail,
345                                ca);
346   GNUNET_MQ_destroy (ca->mq);
347   GNUNET_free (ca);
348 }
349
350
351 /**
352  * Address-callback, used to send message to gnunet-nat-server.
353  *
354  * @param cls closure
355  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
356  *     the previous (now invalid) one
357  * @param ac address class the address belongs to
358  * @param addr either the previous or the new public IP address
359  * @param addrlen actual length of the @a addr
360  */
361 static void
362 addr_cb (void *cls,
363          int add_remove,
364          enum GNUNET_NAT_AddressClass ac,
365          const struct sockaddr *addr,
366          socklen_t addrlen)
367 {
368   struct GNUNET_NAT_AUTO_Test *h = cls;
369   struct ClientActivity *ca;
370   struct GNUNET_MQ_Envelope *env;
371   struct GNUNET_NAT_AUTO_TestMessage *msg;
372   const struct sockaddr_in *sa;
373
374   if (GNUNET_YES != add_remove)
375     return;
376   if (addrlen != sizeof (struct sockaddr_in))
377   {
378     LOG (GNUNET_ERROR_TYPE_DEBUG,
379          "NAT test ignores IPv6 address `%s' returned from NAT library\n",
380          GNUNET_a2s (addr,
381                      addrlen));
382     return;                     /* ignore IPv6 here */
383   }
384   LOG (GNUNET_ERROR_TYPE_INFO,
385        "Asking gnunet-nat-server to connect to `%s'\n",
386        GNUNET_a2s (addr,
387                    addrlen));
388
389   ca = GNUNET_new (struct ClientActivity);
390   ca->h = h;
391   ca->mq = GNUNET_CLIENT_connect (h->cfg,
392                                   "gnunet-nat-server",
393                                   NULL,
394                                   &mq_error_handler,
395                                   ca);
396   if (NULL == ca->mq)
397   {
398     GNUNET_free (ca);
399     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
400                 _("Failed to connect to `gnunet-nat-server'\n"));
401     return;
402   }
403   GNUNET_CONTAINER_DLL_insert (h->ca_head,
404                                h->ca_tail,
405                                ca);
406   sa = (const struct sockaddr_in *) addr;
407   env = GNUNET_MQ_msg (msg,
408                        GNUNET_MESSAGE_TYPE_NAT_TEST);
409   msg->dst_ipv4 = sa->sin_addr.s_addr;
410   msg->dport = sa->sin_port;
411   msg->data = h->data;
412   msg->is_tcp = htonl ((uint32_t) (h->proto == IPPROTO_TCP));
413   GNUNET_MQ_send (ca->mq,
414                   env);
415 }
416
417
418 /**
419  * Calls the report-callback reporting failure.
420  *
421  * Destroys the nat handle after the callback has been processed.
422  *
423  * @param cls handle to the timed out NAT test
424  */
425 static void
426 do_fail (void *cls)
427 {
428   struct GNUNET_NAT_AUTO_Test *nh = cls;
429
430   nh->ttask = NULL;
431   nh->report (nh->report_cls,
432               nh->status);
433 }
434
435
436 /**
437  * Start testing if NAT traversal works using the given configuration.
438  *  The transport adapters should be down while using this function.
439  *
440  * @param cfg configuration for the NAT traversal
441  * @param proto protocol to test, i.e. IPPROTO_TCP or IPPROTO_UDP
442  * @param section_name configuration section to use for configuration
443  * @param report function to call with the result of the test
444  * @param report_cls closure for @a report
445  * @return handle to cancel NAT test
446  */
447 struct GNUNET_NAT_AUTO_Test *
448 GNUNET_NAT_AUTO_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
449                             uint8_t proto,
450                             const char *section_name,
451                             GNUNET_NAT_TestCallback report,
452                             void *report_cls)
453 {
454   struct GNUNET_NAT_AUTO_Test *nh;
455   unsigned long long bnd_port;
456   struct sockaddr_in sa;
457   const struct sockaddr *addrs[] = {
458     (const struct sockaddr *) &sa
459   };
460   const socklen_t addrlens[] = {
461     sizeof (sa)
462   };
463
464   if ( (GNUNET_OK !=
465         GNUNET_CONFIGURATION_get_value_number (cfg,
466                                                section_name,
467                                                "PORT",
468                                                &bnd_port)) ||
469        (bnd_port > 65535) )
470   {
471     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
472                 _("Failed to find valid PORT in section `%s'\n"),
473                 section_name);
474     return NULL;
475   }
476
477   memset (&sa, 0, sizeof (sa));
478   sa.sin_family = AF_INET;
479   sa.sin_port = htons ((uint16_t) bnd_port);
480 #if HAVE_SOCKADDR_IN_SIN_LEN
481   sa.sin_len = sizeof (sa);
482 #endif
483
484   nh = GNUNET_new (struct GNUNET_NAT_AUTO_Test);
485   nh->cfg = cfg;
486   nh->proto = proto;
487   nh->section_name = GNUNET_strdup (section_name);
488   nh->report = report;
489   nh->report_cls = report_cls;
490   nh->status = GNUNET_NAT_ERROR_SUCCESS;
491   if (0 == bnd_port)
492   {
493     nh->nat
494       = GNUNET_NAT_register (cfg,
495                              section_name,
496                              proto,
497                              0, NULL, NULL,
498                              &addr_cb,
499                              &reversal_cb,
500                              nh);
501   }
502   else
503   {
504     nh->lsock
505       = GNUNET_NETWORK_socket_create (AF_INET,
506                                       (IPPROTO_UDP == proto)
507                                       ? SOCK_DGRAM
508                                       : SOCK_STREAM,
509                                       proto);
510     if ( (NULL == nh->lsock) ||
511          (GNUNET_OK !=
512           GNUNET_NETWORK_socket_bind (nh->lsock,
513                                       (const struct sockaddr *) &sa,
514                                       sizeof (sa))))
515     {
516       LOG (GNUNET_ERROR_TYPE_ERROR,
517            _("Failed to create 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_fail,
528                                             nh);
529       return nh;
530     }
531     if (IPPROTO_TCP == proto)
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 %llu (%s)\n",
552          bnd_port,
553          (IPPROTO_TCP == proto) ? "tcp" : "udp");
554     nh->nat = GNUNET_NAT_register (cfg,
555                                    section_name,
556                                    proto,
557                                    1,
558                                    addrs,
559                                    addrlens,
560                                    &addr_cb,
561                                    NULL,
562                                    nh);
563     if (NULL == nh->nat)
564     {
565       LOG (GNUNET_ERROR_TYPE_INFO,
566           _("NAT test failed to start NAT library\n"));
567       if (NULL != nh->ltask)
568       {
569         GNUNET_SCHEDULER_cancel (nh->ltask);
570         nh->ltask = NULL;
571       }
572       if (NULL != nh->lsock)
573       {
574         GNUNET_NETWORK_socket_close (nh->lsock);
575         nh->lsock = NULL;
576       }
577       nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED;
578       nh->ttask = GNUNET_SCHEDULER_add_now (&do_fail,
579                                             nh);
580       return nh;
581     }
582   }
583   return nh;
584 }
585
586
587 /**
588  * Stop an active NAT test.
589  *
590  * @param tst test to stop.
591  */
592 void
593 GNUNET_NAT_AUTO_test_stop (struct GNUNET_NAT_AUTO_Test *tst)
594 {
595   struct NatActivity *pos;
596   struct ClientActivity *cpos;
597
598   LOG (GNUNET_ERROR_TYPE_DEBUG,
599        "Stopping NAT test\n");
600   while (NULL != (cpos = tst->ca_head))
601   {
602     GNUNET_CONTAINER_DLL_remove (tst->ca_head,
603                                  tst->ca_tail,
604                                  cpos);
605     GNUNET_MQ_destroy (cpos->mq);
606     GNUNET_free (cpos);
607   }
608   while (NULL != (pos = tst->na_head))
609   {
610     GNUNET_CONTAINER_DLL_remove (tst->na_head,
611                                  tst->na_tail,
612                                  pos);
613     GNUNET_SCHEDULER_cancel (pos->rtask);
614     GNUNET_NETWORK_socket_close (pos->sock);
615     GNUNET_free (pos);
616   }
617   if (NULL != tst->ttask)
618   {
619     GNUNET_SCHEDULER_cancel (tst->ttask);
620     tst->ttask = NULL;
621   }
622   if (NULL != tst->ltask)
623   {
624     GNUNET_SCHEDULER_cancel (tst->ltask);
625     tst->ltask = NULL;
626   }
627   if (NULL != tst->lsock)
628   {
629     GNUNET_NETWORK_socket_close (tst->lsock);
630     tst->lsock = NULL;
631   }
632   if (NULL != tst->nat)
633   {
634     GNUNET_NAT_unregister (tst->nat);
635     tst->nat = NULL;
636   }
637   GNUNET_free (tst->section_name);
638   GNUNET_free (tst);
639 }
640
641 /* end of nat_auto_api_test.c */