first batch of license fixes (boring)
[oweals/gnunet.git] / src / nat / gnunet-nat.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2015, 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 src/nat/gnunet-nat.c
18  * @brief Command-line tool to interact with the NAT service
19  * @author Christian Grothoff
20  * @author Bruno Cabral
21  */
22 #include "platform.h"
23 #include "gnunet_util_lib.h"
24 #include "gnunet_nat_service.h"
25
26 /**
27  * Value to return from #main().
28  */
29 static int global_ret;
30
31 /**
32  * Name of section in configuration file to use for
33  * additional options.
34  */
35 static char *section_name;
36
37 /**
38  * Flag set to 1 if we use IPPROTO_UDP.
39  */
40 static int use_udp;
41
42 /**
43  * Flag set to 1 if we are to listen for connection reversal requests.
44  */
45 static int listen_reversal;
46
47 /**
48  * Flag set to 1 if we use IPPROTO_TCP.
49  */
50 static int use_tcp;
51
52 /**
53  * Protocol to use.
54  */
55 static uint8_t proto;
56
57 /**
58  * Local address to use for connection reversal request.
59  */
60 static char *local_addr;
61
62 /**
63  * Remote address to use for connection reversal request.
64  */
65 static char *remote_addr;
66
67 /**
68  * Should we actually bind to #bind_addr and receive and process STUN requests?
69  */
70 static int do_stun;
71
72 /**
73  * Handle to NAT operation.
74  */
75 static struct GNUNET_NAT_Handle *nh;
76
77 /**
78  * Listen socket for STUN processing.
79  */
80 static struct GNUNET_NETWORK_Handle *ls;
81
82 /**
83  * Task for reading STUN packets.
84  */
85 static struct GNUNET_SCHEDULER_Task *rtask;
86
87
88 /**
89  * Test if all activities have finished, and if so,
90  * terminate.
91  */
92 static void
93 test_finished ()
94 {
95   if (NULL != nh)
96     return;
97   if (NULL != rtask)
98     return;
99   GNUNET_SCHEDULER_shutdown ();
100 }
101
102
103 /**
104  * Signature of the callback passed to #GNUNET_NAT_register() for
105  * a function to call whenever our set of 'valid' addresses changes.
106  *
107  * @param cls closure, NULL
108  * @param add_remove #GNUNET_YES to add a new public IP address,
109  *                   #GNUNET_NO to remove a previous (now invalid) one
110  * @param ac address class the address belongs to
111  * @param addr either the previous or the new public IP address
112  * @param addrlen actual length of the @a addr
113  */
114 static void
115 address_cb (void *cls,
116             int add_remove,
117             enum GNUNET_NAT_AddressClass ac,
118             const struct sockaddr *addr,
119             socklen_t addrlen)
120 {
121   fprintf (stdout,
122            "%s %s (%d)\n",
123            add_remove ? "+" : "-",
124            GNUNET_a2s (addr,
125                        addrlen),
126            (int) ac);
127 }
128
129
130 /**
131  * Signature of the callback passed to #GNUNET_NAT_register().
132  * for a function to call whenever someone asks us to do connection
133  * reversal.
134  *
135  * @param cls closure, NULL
136  * @param remote_addr public IP address of the other peer
137  * @param remote_addrlen actual length of the @a remote_addr
138  */
139 static void
140 reversal_cb (void *cls,
141              const struct sockaddr *remote_addr,
142              socklen_t remote_addrlen)
143 {
144   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
145               "Connection reversal requested by %s\n",
146               GNUNET_a2s (remote_addr,
147                           remote_addrlen));
148 }
149
150
151 /**
152  * Task run on shutdown.
153  *
154  * @param cls NULL
155  */
156 static void
157 do_shutdown (void *cls)
158 {
159   if (NULL != nh)
160   {
161     GNUNET_NAT_unregister (nh);
162     nh = NULL;
163   }
164   if (NULL != ls)
165   {
166     GNUNET_NETWORK_socket_close (ls);
167     ls = NULL;
168   }
169   if (NULL != rtask)
170   {
171     GNUNET_SCHEDULER_cancel (rtask);
172     rtask = NULL;
173   }
174 }
175
176
177 /**
178  * Task to receive incoming packets for STUN processing.
179  */
180 static void
181 stun_read_task (void *cls)
182 {
183   ssize_t size;
184
185   rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
186                                          ls,
187                                          &stun_read_task,
188                                          NULL);
189   size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
190   if (size > 0)
191   {
192     GNUNET_break (0);
193     GNUNET_SCHEDULER_shutdown ();
194     global_ret = 1;
195     return;
196   }
197   {
198     char buf[size + 1];
199     struct sockaddr_storage sa;
200     socklen_t salen = sizeof (sa);
201     ssize_t ret;
202
203     ret = GNUNET_NETWORK_socket_recvfrom (ls,
204                                           buf,
205                                           size + 1,
206                                           (struct sockaddr *) &sa,
207                                           &salen);
208     if (ret != size)
209     {
210       GNUNET_break (0);
211       GNUNET_SCHEDULER_shutdown ();
212       global_ret = 1;
213       return;
214     }
215     (void) GNUNET_NAT_stun_handle_packet (nh,
216                                           (const struct sockaddr *) &sa,
217                                           salen,
218                                           buf,
219                                           ret);
220   }
221 }
222
223
224 /**
225  * Main function that will be run.
226  *
227  * @param cls closure
228  * @param args remaining command-line arguments
229  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
230  * @param c configuration
231  */
232 static void
233 run (void *cls,
234      char *const *args,
235      const char *cfgfile,
236      const struct GNUNET_CONFIGURATION_Handle *c)
237 {
238   uint8_t af;
239   struct sockaddr *local_sa;
240   struct sockaddr *remote_sa;
241   socklen_t local_len;
242   size_t remote_len;
243
244   if (use_tcp && use_udp)
245   {
246     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
247                 "Cannot use TCP and UDP\n");
248     global_ret = 1;
249     return;
250   }
251   proto = 0;
252   if (use_tcp)
253     proto = IPPROTO_TCP;
254   if (use_udp)
255     proto = IPPROTO_UDP;
256
257   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
258                                  NULL);
259
260   if (0 == proto)
261   {
262     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
263                 "Must specify either TCP or UDP\n");
264     global_ret = 1;
265     return;
266   }
267   local_len = 0;
268   local_sa = NULL;
269   remote_len = 0;
270   remote_sa = NULL;
271   if (NULL != local_addr)
272   {
273     local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr,
274                                                               &af,
275                                                               &local_sa);
276     if (0 == local_len)
277     {
278       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
279                   "Invalid socket address `%s'\n",
280                   local_addr);
281       goto fail_and_shutdown;
282     }
283   }
284
285   if (NULL != remote_addr)
286   {
287     remote_len = GNUNET_STRINGS_parse_socket_addr (remote_addr,
288                                                    &af,
289                                                    &remote_sa);
290     if (0 == remote_len)
291     {
292       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
293                   "Invalid socket address `%s'\n",
294                   remote_addr);
295       goto fail_and_shutdown;
296     }
297   }
298
299   if (NULL != local_addr)
300   {
301     if (NULL == section_name)
302       section_name = GNUNET_strdup ("undefined");
303     nh = GNUNET_NAT_register (c,
304                               section_name,
305                               proto,
306                               1,
307                               (const struct sockaddr **) &local_sa,
308                               &local_len,
309                               &address_cb,
310                               (listen_reversal) ? &reversal_cb : NULL,
311                               NULL);
312   }
313   else if (listen_reversal)
314   {
315     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
316                 "Use of `-W` only effective in combination with `-i`\n");
317     goto fail_and_shutdown;
318   }
319
320   if (NULL != remote_addr)
321   {
322     int ret;
323
324     if ( (NULL == nh) ||
325          (sizeof (struct sockaddr_in) != local_len) )
326     {
327       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
328                   "Require IPv4 local address to initiate connection reversal\n");
329       goto fail_and_shutdown;
330     }
331     if (sizeof (struct sockaddr_in) != remote_len)
332     {
333       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
334                   "Require IPv4 reversal target address\n");
335       goto fail_and_shutdown;
336     }
337     GNUNET_assert (AF_INET == local_sa->sa_family);
338     GNUNET_assert (AF_INET == remote_sa->sa_family);
339     ret = GNUNET_NAT_request_reversal (nh,
340                                        (const struct sockaddr_in *) local_sa,
341                                        (const struct sockaddr_in *) remote_sa);
342     switch (ret)
343     {
344     case GNUNET_SYSERR:
345       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
346                   "Connection reversal internal error\n");
347       break;
348     case GNUNET_NO:
349       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
350                   "Connection reversal unavailable\n");
351       break;
352     case GNUNET_OK:
353       /* operation in progress */
354       break;
355     }
356   }
357
358   if (do_stun)
359   {
360     if (NULL == local_addr)
361     {
362       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
363                   "Require local address to support STUN requests\n");
364       goto fail_and_shutdown;
365     }
366     if (IPPROTO_UDP != proto)
367     {
368       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
369                   "STUN only supported over UDP\n");
370       goto fail_and_shutdown;
371     }
372     ls = GNUNET_NETWORK_socket_create (af,
373                                        SOCK_DGRAM,
374                                        IPPROTO_UDP);
375     if (NULL == ls)
376     {
377       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
378       "Failed to create socket\n");
379       goto fail_and_shutdown;
380     }
381     if (GNUNET_OK !=
382         GNUNET_NETWORK_socket_bind (ls,
383                                     local_sa,
384                                     local_len))
385     {
386       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
387                   "Failed to bind to %s: %s\n",
388                   GNUNET_a2s (local_sa,
389                               local_len),
390                   STRERROR (errno));
391       goto fail_and_shutdown;
392     }
393     rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
394                                            ls,
395                                            &stun_read_task,
396                                            NULL);
397   }
398   GNUNET_free_non_null (remote_sa);
399   GNUNET_free_non_null (local_sa);
400   test_finished ();
401   return;
402  fail_and_shutdown:
403   global_ret = 1;
404   GNUNET_SCHEDULER_shutdown ();
405   GNUNET_free_non_null (remote_sa);
406   GNUNET_free_non_null (local_sa);
407 }
408
409
410 /**
411  * Main function of gnunet-nat
412  *
413  * @param argc number of command-line arguments
414  * @param argv command line
415  * @return 0 on success, -1 on error
416  */
417 int
418 main (int argc,
419       char *const argv[])
420 {
421   struct GNUNET_GETOPT_CommandLineOption options[] = {
422
423     GNUNET_GETOPT_option_string ('i',
424                                  "in",
425                                  "ADDRESS",
426                                  gettext_noop ("which IP and port are we locally using to bind/listen to"),
427                                  &local_addr),
428
429     GNUNET_GETOPT_option_string ('r',
430                                  "remote",
431                                  "ADDRESS",
432                                  gettext_noop ("which remote IP and port should be asked for connection reversal"),
433                                  &remote_addr),
434
435     GNUNET_GETOPT_option_string ('S',
436                                  "section",
437                                  NULL,
438                                  gettext_noop ("name of configuration section to find additional options, such as manual host punching data"),
439                                  &section_name),
440
441     GNUNET_GETOPT_option_flag ('s',
442                                   "stun",
443                                   gettext_noop ("enable STUN processing"),
444                                   &do_stun),
445
446     GNUNET_GETOPT_option_flag ('t',
447                                   "tcp",
448                                   gettext_noop ("use TCP"),
449                                   &use_tcp),
450
451     GNUNET_GETOPT_option_flag ('u',
452                                   "udp",
453                                   gettext_noop ("use UDP"),
454                                   &use_udp),
455
456     GNUNET_GETOPT_option_flag ('W',
457                                   "watch",
458                                   gettext_noop ("watch for connection reversal requests"),
459                                   &listen_reversal),
460     GNUNET_GETOPT_OPTION_END
461   };
462
463   if (GNUNET_OK !=
464       GNUNET_STRINGS_get_utf8_args (argc, argv,
465                                     &argc, &argv))
466     return 2;
467   if (GNUNET_OK !=
468       GNUNET_PROGRAM_run (argc, argv,
469                           "gnunet-nat [options]",
470                           _("GNUnet NAT traversal autoconfigure daemon"),
471                           options,
472                           &run,
473                           NULL))
474   {
475     global_ret = 1;
476   }
477   GNUNET_free ((void*) argv);
478   return global_ret;
479 }
480
481
482 /* end of gnunet-nat.c */