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