tighten formatting rules
[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      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 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_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 app_ctx[in,out] 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 */