error handling
[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 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 /**
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 app_ctx[in,out] location where the app can store stuff
114  *                  on add and retrieve it on remove
115  * @param add_remove #GNUNET_YES to add a new public IP address,
116  *                   #GNUNET_NO to remove a previous (now invalid) one
117  * @param ac address class the address belongs to
118  * @param addr either the previous or the new public IP address
119  * @param addrlen actual length of the @a addr
120  */
121 static void
122 address_cb (void *cls,
123             void **app_ctx,
124             int add_remove,
125             enum GNUNET_NAT_AddressClass ac,
126             const struct sockaddr *addr,
127             socklen_t addrlen)
128 {
129   (void) cls;
130   (void) app_ctx;
131
132   fprintf (stdout,
133            "%s %s (%d)\n",
134            add_remove ? "+" : "-",
135            GNUNET_a2s (addr, addrlen),
136            (int) ac);
137 }
138
139
140 /**
141  * Signature of the callback passed to #GNUNET_NAT_register().
142  * for a function to call whenever someone asks us to do connection
143  * reversal.
144  *
145  * @param cls closure, NULL
146  * @param remote_addr public IP address of the other peer
147  * @param remote_addrlen actual length of the @a remote_addr
148  */
149 static void
150 reversal_cb (void *cls,
151              const struct sockaddr *remote_addr,
152              socklen_t remote_addrlen)
153 {
154   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
155               "Connection reversal requested by %s\n",
156               GNUNET_a2s (remote_addr, remote_addrlen));
157 }
158
159
160 /**
161  * Task run on shutdown.
162  *
163  * @param cls NULL
164  */
165 static void
166 do_shutdown (void *cls)
167 {
168   if (NULL != nh)
169   {
170     GNUNET_NAT_unregister (nh);
171     nh = NULL;
172   }
173   if (NULL != ls)
174   {
175     GNUNET_NETWORK_socket_close (ls);
176     ls = NULL;
177   }
178   if (NULL != rtask)
179   {
180     GNUNET_SCHEDULER_cancel (rtask);
181     rtask = NULL;
182   }
183 }
184
185
186 /**
187  * Task to receive incoming packets for STUN processing.
188  */
189 static void
190 stun_read_task (void *cls)
191 {
192   ssize_t size;
193
194   rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
195                                          ls,
196                                          &stun_read_task,
197                                          NULL);
198   size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
199   if (size > 0)
200   {
201     GNUNET_break (0);
202     GNUNET_SCHEDULER_shutdown ();
203     global_ret = 1;
204     return;
205   }
206   {
207     char buf[size + 1];
208     struct sockaddr_storage sa;
209     socklen_t salen = sizeof(sa);
210     ssize_t ret;
211
212     ret = GNUNET_NETWORK_socket_recvfrom (ls,
213                                           buf,
214                                           size + 1,
215                                           (struct sockaddr *) &sa,
216                                           &salen);
217     if (ret != size)
218     {
219       GNUNET_break (0);
220       GNUNET_SCHEDULER_shutdown ();
221       global_ret = 1;
222       return;
223     }
224     (void) GNUNET_NAT_stun_handle_packet (nh,
225                                           (const struct sockaddr *) &sa,
226                                           salen,
227                                           buf,
228                                           ret);
229   }
230 }
231
232
233 /**
234  * Main function that will be run.
235  *
236  * @param cls closure
237  * @param args remaining command-line arguments
238  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
239  * @param c configuration
240  */
241 static void
242 run (void *cls,
243      char *const *args,
244      const char *cfgfile,
245      const struct GNUNET_CONFIGURATION_Handle *c)
246 {
247   uint8_t af;
248   struct sockaddr *local_sa;
249   struct sockaddr *remote_sa;
250   socklen_t local_len;
251   size_t remote_len;
252
253   if (use_tcp && use_udp)
254   {
255     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n");
256     global_ret = 1;
257     return;
258   }
259   proto = 0;
260   if (use_tcp)
261     proto = IPPROTO_TCP;
262   if (use_udp)
263     proto = IPPROTO_UDP;
264
265   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
266
267   if (0 == proto)
268   {
269     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n");
270     global_ret = 1;
271     return;
272   }
273   local_len = 0;
274   local_sa = NULL;
275   remote_len = 0;
276   remote_sa = NULL;
277   if (NULL != local_addr)
278   {
279     local_len =
280       (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &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 =
293       GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa);
294     if (0 == remote_len)
295     {
296       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
297                   "Invalid socket address `%s'\n",
298                   remote_addr);
299       goto fail_and_shutdown;
300     }
301   }
302
303   if (NULL != local_addr)
304   {
305     if (NULL == section_name)
306       section_name = GNUNET_strdup ("undefined");
307     nh = GNUNET_NAT_register (c,
308                               section_name,
309                               proto,
310                               1,
311                               (const struct sockaddr **) &local_sa,
312                               &local_len,
313                               &address_cb,
314                               (listen_reversal) ? &reversal_cb : NULL,
315                               NULL);
316   }
317   else if (listen_reversal)
318   {
319     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
320                 "Use of `-W` only effective in combination with `-i`\n");
321     goto fail_and_shutdown;
322   }
323
324   if (NULL != remote_addr)
325   {
326     int ret;
327
328     if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len))
329     {
330       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
331                   "Require IPv4 local address to initiate connection reversal\n");
332       goto fail_and_shutdown;
333     }
334     if (sizeof(struct sockaddr_in) != remote_len)
335     {
336       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
337                   "Require IPv4 reversal target address\n");
338       goto fail_and_shutdown;
339     }
340     GNUNET_assert (AF_INET == local_sa->sa_family);
341     GNUNET_assert (AF_INET == remote_sa->sa_family);
342     ret = GNUNET_NAT_request_reversal (nh,
343                                        (const struct sockaddr_in *) local_sa,
344                                        (const struct sockaddr_in *) remote_sa);
345     switch (ret)
346     {
347     case GNUNET_SYSERR:
348       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
349                   "Connection reversal internal error\n");
350       break;
351
352     case GNUNET_NO:
353       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
354                   "Connection reversal unavailable\n");
355       break;
356
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, "STUN only supported over UDP\n");
374       goto fail_and_shutdown;
375     }
376     ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP);
377     if (NULL == ls)
378     {
379       GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n");
380       goto fail_and_shutdown;
381     }
382     if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len))
383     {
384       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385                   "Failed to bind to %s: %s\n",
386                   GNUNET_a2s (local_sa, local_len),
387                   strerror (errno));
388       goto fail_and_shutdown;
389     }
390     rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
391                                            ls,
392                                            &stun_read_task,
393                                            NULL);
394   }
395   GNUNET_free_non_null (remote_sa);
396   GNUNET_free_non_null (local_sa);
397   test_finished ();
398   return;
399 fail_and_shutdown:
400   global_ret = 1;
401   GNUNET_SCHEDULER_shutdown ();
402   GNUNET_free_non_null (remote_sa);
403   GNUNET_free_non_null (local_sa);
404 }
405
406
407 /**
408  * Main function of gnunet-nat
409  *
410  * @param argc number of command-line arguments
411  * @param argv command line
412  * @return 0 on success, -1 on error
413  */
414 int
415 main (int argc, char *const argv[])
416 {
417   struct GNUNET_GETOPT_CommandLineOption options[] = {
418     GNUNET_GETOPT_option_string (
419       'i',
420       "in",
421       "ADDRESS",
422       gettext_noop ("which IP and port are we locally using to bind/listen to"),
423       &local_addr),
424
425     GNUNET_GETOPT_option_string (
426       'r',
427       "remote",
428       "ADDRESS",
429       gettext_noop (
430         "which remote IP and port should be asked for connection reversal"),
431       &remote_addr),
432
433     GNUNET_GETOPT_option_string (
434       'S',
435       "section",
436       NULL,
437       gettext_noop (
438         "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', "tcp", gettext_noop ("use TCP"), &use_tcp),
447
448     GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp),
449
450     GNUNET_GETOPT_option_flag ('W',
451                                "watch",
452                                gettext_noop (
453                                  "watch for connection reversal requests"),
454                                &listen_reversal),
455     GNUNET_GETOPT_OPTION_END
456   };
457
458   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
459     return 2;
460   if (GNUNET_OK !=
461       GNUNET_PROGRAM_run (argc,
462                           argv,
463                           "gnunet-nat [options]",
464                           _ ("GNUnet NAT traversal autoconfigure daemon"),
465                           options,
466                           &run,
467                           NULL))
468   {
469     global_ret = 1;
470   }
471   GNUNET_free ((void *) argv);
472   return global_ret;
473 }
474
475
476 /* end of gnunet-nat.c */