first batch of license fixes (boring)
[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 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 /**
17  * @file nat/nat_test.c
18  * @brief functions to test if the NAT configuration is successful at achieving NAT traversal (with the help of a gnunet-nat-server)
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_util_lib.h"
23 #include "gnunet_nat_lib.h"
24 #include "nat.h"
25
26 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __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_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_Test *h;
86
87 };
88
89
90 /**
91  * Handle to a NAT test.
92  */
93 struct GNUNET_NAT_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    * #GNUNET_YES if we're testing TCP
153    */
154   int is_tcp;
155
156   /**
157    * Data that should be transmitted or source-port.
158    */
159   uint16_t data;
160
161   /**
162    * Advertised port to the other peer.
163    */
164   uint16_t adv_port;
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_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_Test`
209  */
210 static void
211 do_udp_read (void *cls)
212 {
213   struct GNUNET_NAT_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_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_Test`
292  */
293 static void
294 do_accept (void *cls)
295 {
296   struct GNUNET_NAT_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_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 addr either the previous or the new public IP address
358  * @param addrlen actual length of the @a addr
359  */
360 static void
361 addr_cb (void *cls,
362          int add_remove,
363          const struct sockaddr *addr,
364          socklen_t addrlen)
365 {
366   struct GNUNET_NAT_Test *h = cls;
367   struct ClientActivity *ca;
368   struct GNUNET_MQ_Envelope *env;
369   struct GNUNET_NAT_TestMessage *msg;
370   const struct sockaddr_in *sa;
371
372   if (GNUNET_YES != add_remove)
373     return;
374   if (addrlen != sizeof (struct sockaddr_in))
375   {
376     LOG (GNUNET_ERROR_TYPE_DEBUG,
377          "NAT test ignores IPv6 address `%s' returned from NAT library\n",
378          GNUNET_a2s (addr,
379                      addrlen));
380     return;                     /* ignore IPv6 here */
381   }
382   LOG (GNUNET_ERROR_TYPE_INFO,
383        "Asking gnunet-nat-server to connect to `%s'\n",
384        GNUNET_a2s (addr,
385                    addrlen));
386
387   ca = GNUNET_new (struct ClientActivity);
388   ca->h = h;
389   ca->mq = GNUNET_CLIENT_connect (h->cfg,
390                                   "gnunet-nat-server",
391                                   NULL,
392                                   &mq_error_handler,
393                                   ca);
394   if (NULL == ca->mq)
395   {
396     GNUNET_free (ca);
397     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
398                 _("Failed to connect to `gnunet-nat-server'\n"));
399     return;
400   }
401   GNUNET_CONTAINER_DLL_insert (h->ca_head,
402                                h->ca_tail,
403                                ca);
404   sa = (const struct sockaddr_in *) addr;
405   env = GNUNET_MQ_msg (msg,
406                        GNUNET_MESSAGE_TYPE_NAT_TEST);
407   msg->dst_ipv4 = sa->sin_addr.s_addr;
408   msg->dport = sa->sin_port;
409   msg->data = h->data;
410   msg->is_tcp = htonl ((uint32_t) h->is_tcp);
411   GNUNET_MQ_send (ca->mq,
412                   env);
413 }
414
415
416 /**
417  * Timeout task for a nat test.
418  * Calls the report-callback with a timeout return value
419  *
420  * Destroys the nat handle after the callback has been processed.
421  *
422  * @param cls handle to the timed out NAT test
423  */
424 static void
425 do_timeout (void *cls)
426 {
427   struct GNUNET_NAT_Test *nh = cls;
428
429   nh->ttask = NULL;
430   nh->report (nh->report_cls,
431               (GNUNET_NAT_ERROR_SUCCESS == nh->status)
432               ? GNUNET_NAT_ERROR_TIMEOUT
433               : nh->status);
434 }
435
436
437 /**
438  * Start testing if NAT traversal works using the
439  * given configuration (IPv4-only).
440  *
441  * ALL failures are reported directly to the report callback
442  *
443  * @param cfg configuration for the NAT traversal
444  * @param is_tcp #GNUNET_YES to test TCP, #GNUNET_NO to test UDP
445  * @param bnd_port port to bind to, 0 for connection reversal
446  * @param adv_port externally advertised port to use
447  * @param timeout delay after which the test should be aborted
448  * @param report function to call with the result of the test
449  * @param report_cls closure for @a report
450  * @return handle to cancel NAT test or NULL. The error is always indicated via the report callback
451  */
452 struct GNUNET_NAT_Test *
453 GNUNET_NAT_test_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
454                        int is_tcp,
455                        uint16_t bnd_port,
456                        uint16_t adv_port,
457                        struct GNUNET_TIME_Relative timeout,
458                        GNUNET_NAT_TestCallback report,
459                        void *report_cls)
460 {
461   struct GNUNET_NAT_Test *nh;
462   struct sockaddr_in sa;
463   const struct sockaddr *addrs[] = {
464     (const struct sockaddr *) &sa
465   };
466   const socklen_t addrlens[] = {
467     sizeof (sa)
468   };
469
470   memset (&sa, 0, sizeof (sa));
471   sa.sin_family = AF_INET;
472   sa.sin_port = htons (bnd_port);
473 #if HAVE_SOCKADDR_IN_SIN_LEN
474   sa.sin_len = sizeof (sa);
475 #endif
476
477   nh = GNUNET_new (struct GNUNET_NAT_Test);
478   nh->cfg = cfg;
479   nh->is_tcp = is_tcp;
480   nh->data = bnd_port;
481   nh->adv_port = adv_port;
482   nh->report = report;
483   nh->report_cls = report_cls;
484   nh->status = GNUNET_NAT_ERROR_SUCCESS;
485   if (0 == bnd_port)
486   {
487     nh->nat
488       = GNUNET_NAT_register (cfg,
489                              is_tcp,
490                              0,
491                              0,
492                              NULL,
493                              NULL,
494                              &addr_cb,
495                              &reversal_cb,
496                              nh,
497                              NULL);
498   }
499   else
500   {
501     nh->lsock =
502         GNUNET_NETWORK_socket_create (AF_INET,
503                                       (is_tcp ==
504                                        GNUNET_YES) ? SOCK_STREAM : SOCK_DGRAM,
505                                       0);
506     if ((nh->lsock == NULL) ||
507         (GNUNET_OK !=
508          GNUNET_NETWORK_socket_bind (nh->lsock,
509                                      (const struct sockaddr *) &sa,
510                                      sizeof (sa))))
511     {
512       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
513                   _("Failed to create listen socket bound to `%s' for NAT test: %s\n"),
514                   GNUNET_a2s ((const struct sockaddr *) &sa,
515                               sizeof (sa)),
516                   STRERROR (errno));
517       if (NULL != nh->lsock)
518       {
519         GNUNET_NETWORK_socket_close (nh->lsock);
520         nh->lsock = NULL;
521       }
522       nh->status = GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR;
523       nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
524                                             nh);
525       return nh;
526     }
527     if (GNUNET_YES == is_tcp)
528     {
529       GNUNET_break (GNUNET_OK ==
530                     GNUNET_NETWORK_socket_listen (nh->lsock,
531                                                   5));
532       nh->ltask =
533           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
534                                          nh->lsock,
535                                          &do_accept,
536                                          nh);
537     }
538     else
539     {
540       nh->ltask =
541           GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
542                                          nh->lsock,
543                                          &do_udp_read,
544                                          nh);
545     }
546     LOG (GNUNET_ERROR_TYPE_INFO,
547          "NAT test listens on port %u (%s)\n",
548          bnd_port,
549          (GNUNET_YES == is_tcp) ? "tcp" : "udp");
550     nh->nat = GNUNET_NAT_register (cfg,
551                                    is_tcp,
552                                    adv_port,
553                                    1,
554                                    addrs,
555                                    addrlens,
556                                    &addr_cb,
557                                    NULL,
558                                    nh,
559                                    NULL);
560     if (NULL == nh->nat)
561     {
562       LOG (GNUNET_ERROR_TYPE_INFO,
563           _("NAT test failed to start NAT library\n"));
564       if (NULL != nh->ltask)
565       {
566         GNUNET_SCHEDULER_cancel (nh->ltask);
567         nh->ltask = NULL;
568       }
569       if (NULL != nh->lsock)
570       {
571         GNUNET_NETWORK_socket_close (nh->lsock);
572         nh->lsock = NULL;
573       }
574       nh->status = GNUNET_NAT_ERROR_NAT_REGISTER_FAILED;
575       nh->ttask = GNUNET_SCHEDULER_add_now (&do_timeout,
576                                             nh);
577       return nh;
578     }
579   }
580   nh->ttask = GNUNET_SCHEDULER_add_delayed (timeout,
581                                             &do_timeout,
582                                             nh);
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_test_stop (struct GNUNET_NAT_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);
638 }
639
640 /* end of nat_test.c */