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