-doxygen, indentation, fixing comments
[oweals/gnunet.git] / src / nat / nat_auto.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file nat/nat_auto.c
23  * @brief functions for auto-configuration of the network
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_resolver_service.h"
29 #include "gnunet_nat_lib.h"
30 #include "nat.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
33
34
35 /**
36  * How long do we wait for the NAT test to report success?
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
39
40 /**
41  * Phases of the auto configuration.
42  */
43 enum AutoPhase
44 {
45   /**
46    * Initial start value.
47    */
48   AUTO_INIT = 0,
49
50   /**
51    * Test if we are online.
52    */
53   AUTO_ONLINE,
54
55   /**
56    * Test our external IP.
57    */
58   AUTO_EXTERNAL_IP,
59
60   /**
61    * Test our internal IP.
62    */
63   AUTO_LOCAL_IP,
64
65   /**
66    * Test if NAT was punched.
67    */
68   AUTO_NAT_PUNCHED,
69
70   /**
71    * Test if UPnP is working.
72    */
73   AUTO_UPNPC,
74
75   /**
76    * Test if ICMP server works.
77    */
78   AUTO_ICMP_SERVER,
79
80   /**
81    * Test if ICMP client works.
82    */
83   AUTO_ICMP_CLIENT,
84
85   /**
86    * Last phase, we're done.
87    */
88   AUTO_DONE
89
90 };
91
92
93 /**
94  * Handle to auto-configuration in progress.
95  */
96 struct GNUNET_NAT_AutoHandle
97 {
98
99   /**
100    * Handle to the active NAT test.
101    */
102   struct GNUNET_NAT_Test *tst;
103
104   /**
105    * Function to call when done.
106    */
107   GNUNET_NAT_AutoResultCallback fin_cb;
108
109   /**
110    * Closure for @e fin_cb.
111    */
112   void *fin_cb_cls;
113
114   /**
115    * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation.
116    */
117   struct GNUNET_NAT_ExternalHandle *eh;
118
119   /**
120    * Current configuration (with updates from previous phases)
121    */
122   struct GNUNET_CONFIGURATION_Handle *cfg;
123
124   /**
125    * Original configuration (used to calculate differences)
126    */
127   struct GNUNET_CONFIGURATION_Handle *initial_cfg;
128
129   /**
130    * Task identifier for the timeout.
131    */
132   GNUNET_SCHEDULER_TaskIdentifier task;
133
134   /**
135    * Where are we in the test?
136    */
137   enum AutoPhase phase;
138
139   /**
140    * Do we have IPv6?
141    */
142   int have_v6;
143
144 };
145
146
147 /**
148  * Run the next phase of the auto test.
149  *
150  * @param ah auto test handle
151  */
152 static void
153 next_phase (struct GNUNET_NAT_AutoHandle *ah);
154
155
156 /**
157  * Function called if NAT failed to confirm success.
158  * Clean up and update GUI (with failure).
159  *
160  * @param cls closure with setup context
161  * @param tc scheduler callback
162  */
163 static void
164 fail_timeout (void *cls,
165               const struct GNUNET_SCHEDULER_TaskContext *tc)
166 {
167   struct GNUNET_NAT_AutoHandle *ah = cls;
168
169   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
170               _("NAT traversal with ICMP Server timed out.\n"));
171   GNUNET_assert (NULL != ah->tst);
172   ah->task = GNUNET_SCHEDULER_NO_TASK;
173   GNUNET_NAT_test_stop (ah->tst);
174   ah->tst = NULL;
175   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
176                                          "ENABLE_ICMP_SERVER",
177                                          "NO");
178   next_phase (ah);
179 }
180
181
182 /**
183  * Function called by NAT on success.
184  * Clean up and update GUI (with success).
185  *
186  * @param cls the auto handle
187  * @param success currently always #GNUNET_OK
188  */
189 static void
190 result_callback (void *cls, int success)
191 {
192   struct GNUNET_NAT_AutoHandle *ah = cls;
193
194   GNUNET_SCHEDULER_cancel (ah->task);
195   ah->task = GNUNET_SCHEDULER_NO_TASK;
196   GNUNET_NAT_test_stop (ah->tst);
197   ah->tst = NULL;
198   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
199               success
200               ? _("NAT traversal with ICMP Server succeeded.\n")
201               : _("NAT traversal with ICMP Server failed.\n"));
202   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
203                                          success ? "YES": "NO");
204   next_phase (ah);
205 }
206
207
208 /**
209  * Main function for the connection reversal test.
210  *
211  * @param cls the `struct GNUNET_NAT_AutoHandle`
212  * @param tc scheduler context
213  */
214 static void
215 reversal_test (void *cls,
216                const struct GNUNET_SCHEDULER_TaskContext *tc)
217 {
218   struct GNUNET_NAT_AutoHandle *ah = cls;
219
220   ah->task = GNUNET_SCHEDULER_NO_TASK;
221   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
222               _("Testing connection reversal with ICMP server.\n"));
223   GNUNET_RESOLVER_connect (ah->cfg);
224   ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0,
225                                    &result_callback, ah);
226   if (NULL == ah->tst)
227   {
228     next_phase (ah);
229     return;
230   }
231   ah->task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, ah);
232 }
233
234
235 /**
236  * Test if we are online at all.
237  *
238  * @param ah auto setup context
239  */
240 static void
241 test_online (struct GNUNET_NAT_AutoHandle *ah)
242 {
243   // FIXME: not implemented
244   next_phase (ah);
245 }
246
247
248 /**
249  * Set our external IPv4 address.
250  *
251  * @param cls closure with our setup context
252  * @param addr the address, NULL on errors
253  */
254 static void
255 set_external_ipv4 (void *cls,
256                    const struct in_addr *addr)
257 {
258   struct GNUNET_NAT_AutoHandle *ah = cls;
259   char buf[INET_ADDRSTRLEN];
260
261   ah->eh = NULL;
262   if (NULL == addr)
263   {
264     next_phase (ah);
265     return;
266   }
267   /* enable 'behind nat' */
268   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
269               _("Detected external IP `%s'\n"),
270               inet_ntop (AF_INET,
271                          addr,
272                          buf,
273                          sizeof (buf)));
274   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
275
276   /* set external IP address */
277   if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
278   {
279     GNUNET_break (0);
280     next_phase (ah);
281     return;
282   }
283   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
284                                          buf);
285   next_phase (ah);
286 }
287
288
289 /**
290  * Determine our external IPv4 address.
291  *
292  * @param ah auto setup context
293  */
294 static void
295 test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
296 {
297   // FIXME: CPS?
298   /* try to detect external IP */
299   ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
300                                               &set_external_ipv4, ah);
301 }
302
303
304 /**
305  * Process list of local IP addresses.  Find and set the
306  * one of the default interface.
307  *
308  * @param cls our `struct GNUNET_NAT_AutoHandle`
309  * @param name name of the interface (can be NULL for unknown)
310  * @param isDefault is this presumably the default interface
311  * @param addr address of this interface (can be NULL for unknown or unassigned)
312  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
313  * @param netmask the network mask (can be NULL for unknown or unassigned))
314  * @param addrlen length of the @a addr and @a broadcast_addr
315  * @return GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
316  */
317 static int
318 nipo (void *cls,
319       const char *name,
320       int isDefault,
321       const struct sockaddr *addr,
322       const struct sockaddr *broadcast_addr,
323       const struct sockaddr *netmask,
324       socklen_t addrlen)
325 {
326   struct GNUNET_NAT_AutoHandle *ah = cls;
327   const struct sockaddr_in *in;
328   char buf[INET_ADDRSTRLEN];
329
330   if (!isDefault)
331     return GNUNET_OK;
332   if ( (sizeof (struct sockaddr_in6) == addrlen) &&
333        (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
334                      sizeof (struct in6_addr))) &&
335        (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
336   {
337     ah->have_v6 = GNUNET_YES;
338     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
339                 _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
340     return GNUNET_OK;
341   }
342   if (addrlen != sizeof (struct sockaddr_in))
343     return GNUNET_OK;
344   in = (const struct sockaddr_in *) addr;
345
346   /* set internal IP address */
347   if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
348   {
349     GNUNET_break (0);
350     return GNUNET_OK;
351   }
352   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
353                                          buf);
354   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
355               _("Detected internal network address `%s'.\n"),
356               buf);
357   /* no need to continue iteration */
358   return GNUNET_SYSERR;
359 }
360
361
362 /**
363  * Determine our local IP addresses; detect internal IP & IPv6-support
364  *
365  * @param ah auto setup context
366  */
367 static void
368 test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
369 {
370   ah->have_v6 = GNUNET_NO;
371   GNUNET_OS_network_interfaces_list (&nipo, ah);
372   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
373                                          (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
374   next_phase (ah);
375 }
376
377
378 /**
379  * Test if NAT has been punched
380  *
381  * @param ah auto setup context
382  */
383 static void
384 test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
385 {
386   // FIXME: not implemented
387   next_phase (ah);
388 }
389
390
391 /**
392  * Test if UPnPC works.
393  *
394  * @param ah auto setup context
395  */
396 static void
397 test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
398 {
399   int have_upnpc;
400
401   /* test if upnpc is available */
402   have_upnpc = (GNUNET_SYSERR !=
403                 GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
404   /* FIXME: test if upnpc is actually working, that is, if transports
405      start to work once we use UPnP */
406   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
407               (have_upnpc)
408               ? _("upnpc found, enabling its use\n")
409               : _("upnpc not found\n"));
410   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
411                                          (GNUNET_YES == have_upnpc) ? "YES" : "NO");
412   next_phase (ah);
413 }
414
415
416 /**
417  * Test if ICMP server is working
418  *
419  * @param ah auto setup context
420  */
421 static void
422 test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
423 {
424   int hns;
425   char *tmp;
426   char *binary;
427
428   tmp = NULL;
429   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
430   hns =
431       ((GNUNET_OK ==
432         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
433                                                &tmp)) && (0 < strlen (tmp)) &&
434        (GNUNET_YES ==
435         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
436        (GNUNET_YES ==
437         GNUNET_OS_check_helper_binary (binary, GNUNET_YES, "-d 127.0.0.1" ))); // use localhost as source for that one udp-port, ok for testing
438   GNUNET_free_non_null (tmp);
439   GNUNET_free (binary);
440   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
441               (hns)
442               ? _("gnunet-helper-nat-server found, testing it\n")
443               : _("No working gnunet-helper-nat-server found\n"));
444   if (hns)
445     ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, ah);
446   else
447     next_phase (ah);
448 }
449
450
451 /**
452  * Test if ICMP client is working
453  *
454  * @param ah auto setup context
455  */
456 static void
457 test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
458 {
459   int hnc;
460   char *tmp;
461   char *binary;
462
463   tmp = NULL;
464   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
465   hnc =
466       ((GNUNET_OK ==
467         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
468                                                &tmp)) && (0 < strlen (tmp)) &&
469        (GNUNET_YES !=
470         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
471        (GNUNET_YES ==
472         GNUNET_OS_check_helper_binary (binary, GNUNET_YES, "-d 127.0.0.1 127.0.0.2 42"))); // none of these parameters are actually used in privilege testing mode
473   GNUNET_free_non_null (tmp);
474   GNUNET_free (binary);
475   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
476               (hnc)
477               ? _("gnunet-helper-nat-client found, enabling it\n")
478               : _("gnunet-helper-nat-client not found or behind NAT, disabling it\n"));
479   next_phase (ah);
480 }
481
482
483 /**
484  * Run the next phase of the auto test.
485  */
486 static void
487 next_phase (struct GNUNET_NAT_AutoHandle *ah)
488 {
489   struct GNUNET_CONFIGURATION_Handle *diff;
490
491   ah->phase++;
492   switch (ah->phase)
493   {
494   case AUTO_INIT:
495     GNUNET_assert (0);
496     break;
497   case AUTO_ONLINE:
498     test_online (ah);
499     break;
500   case AUTO_EXTERNAL_IP:
501     test_external_ip (ah);
502     break;
503   case AUTO_LOCAL_IP:
504     test_local_ip (ah);
505     break;
506   case AUTO_NAT_PUNCHED:
507     test_nat_punched (ah);
508     break;
509   case AUTO_UPNPC:
510     test_upnpc (ah);
511     break;
512   case AUTO_ICMP_SERVER:
513     test_icmp_server (ah);
514     break;
515   case AUTO_ICMP_CLIENT:
516     test_icmp_client (ah);
517     break;
518   case AUTO_DONE:
519     diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
520                                           ah->cfg);
521     ah->fin_cb (ah->fin_cb_cls,
522                 diff);
523     GNUNET_CONFIGURATION_destroy (diff);
524     GNUNET_NAT_autoconfig_cancel (ah);
525     return;
526   }
527 }
528
529
530 /**
531  * Start auto-configuration routine.  The resolver service should
532  * be available when this function is called.
533  *
534  * @param cfg initial configuration
535  * @param cb function to call with autoconfiguration result
536  * @param cb_cls closure for @a cb
537  * @return handle to cancel operation
538  */
539 struct GNUNET_NAT_AutoHandle *
540 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
541                              GNUNET_NAT_AutoResultCallback cb,
542                              void *cb_cls)
543 {
544   struct GNUNET_NAT_AutoHandle *ah;
545
546   ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
547   ah->fin_cb = cb;
548   ah->fin_cb_cls = cb_cls;
549   ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
550   ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
551
552   /* never use loopback addresses if user wanted autoconfiguration */
553   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
554                                          "USE_LOCALADDR",
555                                          "NO");
556   next_phase (ah);
557   return ah;
558 }
559
560
561 /**
562  * Abort autoconfiguration.
563  *
564  * @param ah handle for operation to abort
565  */
566 void
567 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
568 {
569   if (NULL != ah->tst)
570   {
571     GNUNET_NAT_test_stop (ah->tst);
572     ah->tst = NULL;
573   }
574   if (NULL != ah->eh)
575   {
576     GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
577     ah->eh = NULL;
578   }
579   if (GNUNET_SCHEDULER_NO_TASK != ah->task)
580   {
581     GNUNET_SCHEDULER_cancel (ah->task);
582     ah->task = GNUNET_SCHEDULER_NO_TASK;
583   }
584   GNUNET_CONFIGURATION_destroy (ah->cfg);
585   GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
586   GNUNET_free (ah);
587 }
588
589
590 /* end of nat_auto.c */