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