- fixing channel functions
[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 '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, const struct GNUNET_SCHEDULER_TaskContext *tc)
165 {
166   struct GNUNET_NAT_AutoHandle *ah = cls;
167
168   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
169               _("NAT traversal with ICMP Server timed out.\n"));
170   GNUNET_assert (NULL != ah->tst);
171   ah->task = GNUNET_SCHEDULER_NO_TASK;
172   GNUNET_NAT_test_stop (ah->tst);
173   ah->tst = NULL;
174   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
175                                          "ENABLE_ICMP_SERVER",
176                                          "NO");
177   next_phase (ah);
178 }
179
180
181 /**
182  * Function called by NAT on success.
183  * Clean up and update GUI (with success).
184  *
185  * @param cls the auto handle
186  * @param success currently always GNUNET_OK
187  */
188 static void
189 result_callback (void *cls, int success)
190 {
191   struct GNUNET_NAT_AutoHandle *ah = cls;
192
193   GNUNET_SCHEDULER_cancel (ah->task);
194   ah->task = GNUNET_SCHEDULER_NO_TASK;
195   GNUNET_NAT_test_stop (ah->tst);
196   ah->tst = NULL;
197   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
198               success
199               ? _("NAT traversal with ICMP Server succeeded.\n")
200               : _("NAT traversal with ICMP Server failed.\n"));
201   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
202                                          success ? "YES": "NO");
203   next_phase (ah);
204 }
205
206 /**
207  * Main function for the connection reversal test.
208  *
209  * @param cls the 'int*' for the result
210  * @param tc scheduler context
211  */
212 static void
213 reversal_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
214 {
215   struct GNUNET_NAT_AutoHandle *ah = cls;
216
217   ah->task = GNUNET_SCHEDULER_NO_TASK;
218   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
219               _("Testing connection reversal with ICMP server.\n"));
220   GNUNET_RESOLVER_connect (ah->cfg);
221   ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0,
222                                    &result_callback, ah);
223   if (NULL == ah->tst)
224   {
225     next_phase (ah);
226     return;
227   }
228   ah->task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, ah);
229 }
230
231
232 /**
233  * Test if we are online at all.
234  *
235  * @param ah auto setup context
236  */
237 static void
238 test_online (struct GNUNET_NAT_AutoHandle *ah)
239 {
240   // FIXME: not implemented
241   next_phase (ah);
242 }
243
244
245 /**
246  * Set our external IPv4 address.
247  *
248  * @param cls closure with our setup context
249  * @param addr the address, NULL on errors
250  */
251 static void
252 set_external_ipv4 (void *cls, const struct in_addr *addr)
253 {
254   struct GNUNET_NAT_AutoHandle *ah = cls;
255   char buf[INET_ADDRSTRLEN];
256
257   ah->eh = NULL;
258   if (NULL == addr)
259   {
260     next_phase (ah);
261     return;
262   }
263   /* enable 'behind nat' */
264   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
265               _("Detected external IP `%s'\n"),
266               inet_ntop (AF_INET,
267                          addr,
268                          buf,
269                          sizeof (buf)));
270   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
271
272   /* set external IP address */
273   if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
274   {
275     GNUNET_break (0);
276     next_phase (ah);
277     return;
278   }
279   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
280                                          buf);
281   next_phase (ah);
282 }
283
284
285 /**
286  * Determine our external IPv4 address.
287  *
288  * @param ah auto setup context
289  */
290 static void
291 test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
292 {
293   // FIXME: CPS?
294   /* try to detect external IP */
295   ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
296                                               &set_external_ipv4, ah);
297 }
298
299
300 /**
301  * Process list of local IP addresses.  Find and set the
302  * one of the default interface.
303  *
304  * @param cls our NAT auto handle
305  * @param name name of the interface (can be NULL for unknown)
306  * @param isDefault is this presumably the default interface
307  * @param addr address of this interface (can be NULL for unknown or unassigned)
308  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
309  * @param netmask the network mask (can be NULL for unknown or unassigned))
310  * @param addrlen length of the address
311  * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort
312  */
313 static int
314 nipo (void *cls, const char *name, int isDefault, const struct sockaddr *addr,
315       const struct sockaddr *broadcast_addr, const struct sockaddr *netmask,
316       socklen_t addrlen)
317 {
318   struct GNUNET_NAT_AutoHandle *ah = cls;
319   const struct sockaddr_in *in;
320   char buf[INET_ADDRSTRLEN];
321
322   if (!isDefault)
323     return GNUNET_OK;
324   if ( (sizeof (struct sockaddr_in6) == addrlen) &&
325        (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
326                      sizeof (struct in6_addr))) &&
327        (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
328   {
329     ah->have_v6 = GNUNET_YES;
330     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
331                 _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
332     return GNUNET_OK;
333   }
334   if (addrlen != sizeof (struct sockaddr_in))
335     return GNUNET_OK;
336   in = (const struct sockaddr_in *) addr;
337
338   /* set internal IP address */
339   if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
340   {
341     GNUNET_break (0);
342     return GNUNET_OK;
343   }
344   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
345                                          buf);
346   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
347               _("Detected internal network address `%s'.\n"),
348               buf);
349   /* no need to continue iteration */
350   return GNUNET_SYSERR;
351 }
352
353
354 /**
355  * Determine our local IP addresses; detect internal IP & IPv6-support
356  *
357  * @param ah auto setup context
358  */
359 static void
360 test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
361 {
362   ah->have_v6 = GNUNET_NO;
363   GNUNET_OS_network_interfaces_list (&nipo, ah);
364   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
365                                          (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
366   next_phase (ah);
367 }
368
369
370 /**
371  * Test if NAT has been punched
372  *
373  * @param ah auto setup context
374  */
375 static void
376 test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
377 {
378   // FIXME: not implemented
379   next_phase (ah);
380 }
381
382
383 /**
384  * Test if UPnPC works.
385  *
386  * @param ah auto setup context
387  */
388 static void
389 test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
390 {
391   int have_upnpc;
392
393   /* test if upnpc is available */
394   have_upnpc = (GNUNET_SYSERR !=
395                 GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
396   /* FIXME: test if upnpc is actually working, that is, if transports
397      start to work once we use UPnP */
398   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
399               (have_upnpc)
400               ? _("upnpc found, enabling its use\n")
401               : _("upnpc not found\n"));
402   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
403                                          (GNUNET_YES == have_upnpc) ? "YES" : "NO");
404   next_phase (ah);
405 }
406
407
408 /**
409  * Test if ICMP server is working
410  *
411  * @param ah auto setup context
412  */
413 static void
414 test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
415 {
416   int hns;
417   char *tmp;
418   char *binary;
419
420   tmp = NULL;
421   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
422   hns =
423       ((GNUNET_OK ==
424         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
425                                                &tmp)) && (0 < strlen (tmp)) &&
426        (GNUNET_YES ==
427         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
428        (GNUNET_YES ==
429         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
430   GNUNET_free_non_null (tmp);
431   GNUNET_free (binary);
432   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
433               (hns)
434               ? _("gnunet-helper-nat-server found, testing it\n")
435               : _("No working gnunet-helper-nat-server found\n"));
436   if (hns)
437     ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, ah);
438   else
439     next_phase (ah);
440 }
441
442
443 /**
444  * Test if ICMP client is working
445  *
446  * @param ah auto setup context
447  */
448 static void
449 test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
450 {
451   int hnc;
452   char *tmp;
453   char *binary;
454
455   tmp = NULL;
456   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
457   hnc =
458       ((GNUNET_OK ==
459         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
460                                                &tmp)) && (0 < strlen (tmp)) &&
461        (GNUNET_YES !=
462         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
463        (GNUNET_YES ==
464         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
465   GNUNET_free_non_null (tmp);
466   GNUNET_free (binary);
467   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
468               (hnc)
469               ? _("gnunet-helper-nat-client found, enabling it\n")
470               : _("gnunet-helper-nat-client not found or behind NAT, disabling it\n"));
471   next_phase (ah);
472 }
473
474
475 /**
476  * Run the next phase of the auto test.
477  */
478 static void
479 next_phase (struct GNUNET_NAT_AutoHandle *ah)
480 {
481   struct GNUNET_CONFIGURATION_Handle *diff;
482
483   ah->phase++;
484   switch (ah->phase)
485   {
486   case AUTO_INIT:
487     GNUNET_assert (0);
488     break;
489   case AUTO_ONLINE:
490     test_online (ah);
491     break;
492   case AUTO_EXTERNAL_IP:
493     test_external_ip (ah);
494     break;
495   case AUTO_LOCAL_IP:
496     test_local_ip (ah);
497     break;
498   case AUTO_NAT_PUNCHED:
499     test_nat_punched (ah);
500     break;
501   case AUTO_UPNPC:
502     test_upnpc (ah);
503     break;
504   case AUTO_ICMP_SERVER:
505     test_icmp_server (ah);
506     break;
507   case AUTO_ICMP_CLIENT:
508     test_icmp_client (ah);
509     break;
510   case AUTO_DONE:
511     diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
512                                           ah->cfg);
513     ah->fin_cb (ah->fin_cb_cls,
514                 diff);
515     GNUNET_CONFIGURATION_destroy (diff);
516     GNUNET_NAT_autoconfig_cancel (ah);
517     return;
518   }
519 }
520
521
522
523 /**
524  * Start auto-configuration routine.  The resolver service should
525  * be available when this function is called.
526  *
527  * @param cfg initial configuration
528  * @param cb function to call with autoconfiguration result
529  * @param cb_cls closure for cb
530  * @return handle to cancel operation
531  */
532 struct GNUNET_NAT_AutoHandle *
533 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
534                              GNUNET_NAT_AutoResultCallback cb,
535                              void *cb_cls)
536 {
537   struct GNUNET_NAT_AutoHandle *ah;
538
539   ah = GNUNET_malloc (sizeof (struct GNUNET_NAT_AutoHandle));
540   ah->fin_cb = cb;
541   ah->fin_cb_cls = cb_cls;
542   ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
543   ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
544
545   /* never use loopback addresses if user wanted autoconfiguration */
546   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
547                                          "USE_LOCALADDR",
548                                          "NO");
549   next_phase (ah);
550   return ah;
551 }
552
553                         
554 /**
555  * Abort autoconfiguration.
556  *
557  * @param ah handle for operation to abort
558  */
559 void
560 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
561 {
562   if (NULL != ah->tst)
563   {
564     GNUNET_NAT_test_stop (ah->tst);
565     ah->tst = NULL;
566   }
567   if (NULL != ah->eh)
568   {
569     GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
570     ah->eh = NULL;
571   }
572   if (GNUNET_SCHEDULER_NO_TASK != ah->task)
573   {
574     GNUNET_SCHEDULER_cancel (ah->task);
575     ah->task = GNUNET_SCHEDULER_NO_TASK;
576   }
577   GNUNET_CONFIGURATION_destroy (ah->cfg);
578   GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
579   GNUNET_free (ah);
580 }
581
582
583
584 /* end of nat_auto.c */