cmdline usability for gnunet-social
[oweals/gnunet.git] / src / nat / nat_auto.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2015 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file nat/nat_auto.c
23  * @brief functions for auto-configuration of the network
24  * @author Christian Grothoff
25  * @author Bruno Cabral
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_resolver_service.h"
30 #include "gnunet_nat_lib.h"
31 #include "nat.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
34
35
36 /**
37  * How long do we wait for the NAT test to report success?
38  */
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
40
41 #define NAT_SERVER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
42
43 /**
44  * Phases of the auto configuration.
45  */
46 enum AutoPhase
47 {
48   /**
49    * Initial start value.
50    */
51   AUTO_INIT = 0,
52
53   /**
54    * Test our external IP.
55    */
56   AUTO_EXTERNAL_IP,
57
58   /**
59    * Test our external IP.
60    */
61    AUTO_STUN,
62
63   /**
64    * Test our internal IP.
65    */
66   AUTO_LOCAL_IP,
67
68   /**
69    * Test if NAT was punched.
70    */
71   AUTO_NAT_PUNCHED,
72
73   /**
74    * Test if UPnP is working.
75    */
76   AUTO_UPNPC,
77
78   /**
79    * Test if ICMP server works.
80    */
81   AUTO_ICMP_SERVER,
82
83   /**
84    * Test if ICMP client works.
85    */
86   AUTO_ICMP_CLIENT,
87
88   /**
89    * Last phase, we're done.
90    */
91   AUTO_DONE
92
93 };
94
95
96 /**
97  * Handle to auto-configuration in progress.
98  */
99 struct GNUNET_NAT_AutoHandle
100 {
101
102   /**
103    * Handle to the active NAT test.
104    */
105   struct GNUNET_NAT_Test *tst;
106
107   /**
108    * Function to call when done.
109    */
110   GNUNET_NAT_AutoResultCallback fin_cb;
111
112   /**
113    * Closure for @e fin_cb.
114    */
115   void *fin_cb_cls;
116
117   /**
118    * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation.
119    */
120   struct GNUNET_NAT_ExternalHandle *eh;
121
122   /**
123    * Current configuration (with updates from previous phases)
124    */
125   struct GNUNET_CONFIGURATION_Handle *cfg;
126
127   /**
128    * Original configuration (used to calculate differences)
129    */
130   struct GNUNET_CONFIGURATION_Handle *initial_cfg;
131
132   /**
133    * Task identifier for the timeout.
134    */
135   struct GNUNET_SCHEDULER_Task * task;
136
137   /**
138    * Where are we in the test?
139    */
140   enum AutoPhase phase;
141
142
143   /**
144    * Situation of the NAT
145    */
146   enum GNUNET_NAT_Type type;
147
148   /**
149    * Do we have IPv6?
150    */
151   int have_v6;
152
153   /**
154    * UPnP already set the external ip address ?
155    */
156   int upnp_set_external_address;
157
158   /**
159    * Did the external server connected back ?
160    */
161   int connected_back;
162
163   /**
164     * Address detected by STUN
165    */
166   char* stun_ip;
167   int stun_port;
168
169   /**
170    * Internal IP is the same as the public one ?
171    */
172   int internal_ip_is_public;
173
174   /**
175    * Error code for better debugging and user feedback
176    */
177   enum GNUNET_NAT_StatusCode ret;
178 };
179
180
181
182
183
184
185 /**
186  * The listen socket of the service for IPv4
187  */
188 static struct GNUNET_NETWORK_Handle *lsock4;
189
190
191 /**
192  * The listen task ID for IPv4
193  */
194 static struct GNUNET_SCHEDULER_Task * ltask4;
195
196
197
198
199 /**
200  * The port the test service is running on (default 7895)
201  */
202 static unsigned long long port = 7895;
203
204 static char *stun_server = "stun.ekiga.net";
205 static int stun_port = 3478;
206
207
208
209 /**
210  * Run the next phase of the auto test.
211  *
212  * @param ah auto test handle
213  */
214 static void
215         next_phase (struct GNUNET_NAT_AutoHandle *ah);
216
217
218
219
220 static void
221 process_stun_reply(struct sockaddr_in* answer, struct GNUNET_NAT_AutoHandle *ah)
222 {
223
224   ah->stun_ip = inet_ntoa(answer->sin_addr);
225   ah->stun_port = ntohs(answer->sin_port);
226   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "External IP is: %s , with port %d\n", ah->stun_ip, ah->stun_port);
227
228
229   next_phase (ah);
230
231 }
232
233 /**
234  * Function that terminates the test.
235  */
236 static void
237 stop_stun ()
238 {
239   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping STUN and quitting...\n");
240
241   /* Clean task */
242   if(NULL != ltask4)
243   {
244     GNUNET_SCHEDULER_cancel (ltask4);
245     ltask4 = NULL;
246   }
247
248   /* Clean socket */
249   if(NULL != lsock4)
250   {
251     GNUNET_NETWORK_socket_close (lsock4);
252     lsock4 = NULL;
253   }
254 }
255
256 /**
257  * Activity on our incoming socket.  Read data from the
258  * incoming connection.
259  *
260  * @param cls
261  */
262 static void
263 do_udp_read (void *cls)
264 {
265   struct GNUNET_NAT_AutoHandle *ah = cls;
266   unsigned char reply_buf[1024];
267   ssize_t rlen;
268   struct sockaddr_in answer;
269   const struct GNUNET_SCHEDULER_TaskContext *tc;
270
271   tc = GNUNET_SCHEDULER_get_task_context ();
272   if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
273       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
274                                    lsock4)))
275   {
276     rlen = GNUNET_NETWORK_socket_recv (lsock4,
277                                        reply_buf,
278                                        sizeof (reply_buf));
279
280     //Lets handle the packet
281     memset(&answer, 0, sizeof(struct sockaddr_in));
282     if(ah->phase == AUTO_NAT_PUNCHED)
283     {
284       //Destroy the connection
285       GNUNET_NETWORK_socket_close (lsock4);
286       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
287                   "The external server was able to connect back");
288       ah->connected_back = GNUNET_YES;
289       next_phase (ah);
290     }
291     else
292     {
293       if (GNUNET_OK ==
294           GNUNET_NAT_stun_handle_packet (reply_buf, rlen, &answer))
295       {
296         //Process the answer
297         process_stun_reply (&answer, ah);
298       }
299       else
300       {
301         next_phase (ah);
302       }
303     }
304   }
305   else
306   {
307     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
308                 "TIMEOUT while waiting for an answer\n");
309     if (ah->phase == AUTO_NAT_PUNCHED)
310     {
311       stop_stun();
312     }
313
314     next_phase (ah);
315   }
316 }
317
318
319 /**
320  * Create an IPv4 listen socket bound to our port.
321  *
322  * @return NULL on error
323  */
324 static struct GNUNET_NETWORK_Handle *
325 bind_v4 ()
326 {
327   struct GNUNET_NETWORK_Handle *ls;
328   struct sockaddr_in sa4;
329   int eno;
330
331   memset (&sa4, 0, sizeof (sa4));
332   sa4.sin_family = AF_INET;
333   sa4.sin_port = htons (port);
334 #if HAVE_SOCKADDR_IN_SIN_LEN
335     sa4.sin_len = sizeof (sa4);
336 #endif
337   ls = GNUNET_NETWORK_socket_create (AF_INET,
338                                      SOCK_DGRAM,
339                                      0);
340   if (NULL == ls)
341     return NULL;
342   if (GNUNET_OK !=
343       GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
344                                   sizeof (sa4)))
345   {
346     eno = errno;
347     GNUNET_NETWORK_socket_close (ls);
348     errno = eno;
349     return NULL;
350   }
351   return ls;
352 }
353
354
355 static void
356 request_callback (void *cls,
357                   enum GNUNET_NAT_StatusCode result)
358 {
359   // struct GNUNET_NAT_AutoHandle *ah = cls;
360
361   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Request callback: stop and quit\n");
362   stop_stun ();
363
364   // next_phase (ah); FIXME this always will be NULL, as called in test_stun()
365 }
366
367
368 /**
369  * Function called by NAT to report the outcome of the nat-test.
370  * Clean up and update GUI.
371  *
372  * @param cls the auto handle
373  * @param success currently always #GNUNET_OK
374  * @param emsg NULL on success, otherwise an error message
375  */
376 static void
377 result_callback (void *cls,
378                  enum GNUNET_NAT_StatusCode ret)
379 {
380   struct GNUNET_NAT_AutoHandle *ah = cls;
381
382   if (GNUNET_NAT_ERROR_SUCCESS == ret)
383     GNUNET_NAT_test_stop (ah->tst);
384   ah->tst = NULL;
385   ah->ret = ret;
386   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
387               GNUNET_NAT_ERROR_SUCCESS == ret
388               ? _("NAT traversal with ICMP Server succeeded.\n")
389               : _("NAT traversal with ICMP Server failed.\n"));
390   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
391                                          GNUNET_NAT_ERROR_SUCCESS == ret ? "NO" : "YES");
392   next_phase (ah);
393 }
394
395
396 /**
397  * Main function for the connection reversal test.
398  *
399  * @param cls the `struct GNUNET_NAT_AutoHandle`
400  */
401 static void
402 reversal_test (void *cls)
403 {
404   struct GNUNET_NAT_AutoHandle *ah = cls;
405
406   ah->task = NULL;
407   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
408               _("Testing connection reversal with ICMP server.\n"));
409   GNUNET_RESOLVER_connect (ah->cfg);
410   ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0, TIMEOUT,
411                                    &result_callback, ah);
412 }
413
414
415 /**
416  * Set our external IPv4 address based on the UPnP.
417  *
418  *
419  * @param cls closure with our setup context
420  * @param addr the address, NULL on errors
421  * @param emsg NULL on success, otherwise an error message
422  */
423 static void
424 set_external_ipv4 (void *cls,
425                    const struct in_addr *addr,
426                    enum GNUNET_NAT_StatusCode ret)
427 {
428   struct GNUNET_NAT_AutoHandle *ah = cls;
429   char buf[INET_ADDRSTRLEN];
430
431   ah->eh = NULL;
432   ah->ret = ret;
433   if (GNUNET_NAT_ERROR_SUCCESS != ret)
434   {
435     next_phase (ah);
436     return;
437   }
438   /* enable 'behind nat' */
439   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
440               _("Detected external IP `%s'\n"),
441               inet_ntop (AF_INET,
442                          addr,
443                          buf,
444                          sizeof (buf)));
445   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
446
447   /* set external IP address */
448   if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
449   {
450     GNUNET_break (0);
451     /* actually, this should never happen, as the caller already executed just
452      * this check, but for consistency (eg: future changes in the caller)
453      * we still need to report this error...
454      */
455     ah->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID;
456     next_phase (ah);
457     return;
458   }
459   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
460                                          buf);
461   ah->upnp_set_external_address = GNUNET_YES;
462   next_phase (ah);
463 }
464
465
466 /**
467  * Determine our external IPv4 address.
468  *
469  * @param ah auto setup context
470  */
471 static void
472 test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
473 {
474   if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
475     next_phase (ah);
476
477   // FIXME: CPS?
478   /* try to detect external IP */
479   ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
480                                               &set_external_ipv4, ah);
481 }
482
483
484 /**
485  * Determine our external IPv4 address and port using an external STUN server
486  *
487  * @param ah auto setup context
488  */
489 static void
490 test_stun (struct GNUNET_NAT_AutoHandle *ah)
491 {
492
493   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running STUN test\n");
494
495   /* Get port from the configuration */
496   if (GNUNET_OK !=
497       GNUNET_CONFIGURATION_get_value_number (ah->cfg,
498                                              "transport-udp",
499                                              "PORT",
500                                              &port))
501   {
502     port = 2086;
503   }
504
505   //Lets create the socket
506   lsock4 = bind_v4 ();
507   if (NULL == lsock4)
508   {
509     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
510     next_phase(ah);
511     return;
512   }
513   else
514   {
515     //Lets call our function now when it accepts
516     ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
517                                             lsock4,
518                                             &do_udp_read,
519                                             ah);
520   }
521
522
523   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524               "STUN service listens on port %u\n",
525               (unsigned int) port);
526   if (GNUNET_NO ==
527       GNUNET_NAT_stun_make_request (stun_server, stun_port,
528                                     lsock4,
529                                     &request_callback,
530                                     NULL))
531   {
532     /*An error happened*/
533     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN error, stopping\n");
534     stop_stun ();
535     next_phase (ah);
536   }
537 }
538
539
540 /**
541  * Process list of local IP addresses.  Find and set the
542  * one of the default interface.
543  *
544  * @param cls our `struct GNUNET_NAT_AutoHandle`
545  * @param name name of the interface (can be NULL for unknown)
546  * @param isDefault is this presumably the default interface
547  * @param addr address of this interface (can be NULL for unknown or unassigned)
548  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
549  * @param netmask the network mask (can be NULL for unknown or unassigned))
550  * @param addrlen length of the @a addr and @a broadcast_addr
551  * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
552  */
553 static int
554 process_if (void *cls,
555             const char *name,
556             int isDefault,
557             const struct sockaddr *addr,
558             const struct sockaddr *broadcast_addr,
559             const struct sockaddr *netmask,
560             socklen_t addrlen)
561 {
562   struct GNUNET_NAT_AutoHandle *ah = cls;
563   const struct sockaddr_in *in;
564   char buf[INET_ADDRSTRLEN];
565
566
567   if ( (sizeof (struct sockaddr_in6) == addrlen) &&
568        (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
569                      sizeof (struct in6_addr))) &&
570        (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
571   {
572     ah->have_v6 = GNUNET_YES;
573     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
574                 _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
575
576     return GNUNET_OK;
577   }
578   if (addrlen != sizeof (struct sockaddr_in))
579     return GNUNET_OK;
580   in = (const struct sockaddr_in *) addr;
581
582
583   /* set internal IP address */
584   if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
585   {
586     GNUNET_break (0);
587     return GNUNET_OK;
588   }
589   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
590                                          buf);
591   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
592               _("Detected internal network address `%s'.\n"),
593               buf);
594
595
596   ah->ret = GNUNET_NAT_ERROR_SUCCESS;
597
598   /* Check if our internal IP is the same as the External detect by STUN*/
599   if(ah->stun_ip && (strcmp(buf, ah->stun_ip) == 0) )
600   {
601     ah->internal_ip_is_public = GNUNET_YES;
602     GNUNET_log (GNUNET_ERROR_TYPE_INFO,"A internal IP is the sameas the external");
603     /* No need to continue*/
604     return GNUNET_SYSERR;
605   }
606
607   /* no need to continue iteration if we found the default */
608   if (!isDefault)
609     return GNUNET_OK;
610   else
611     return GNUNET_SYSERR;
612 }
613
614
615 /**
616  * Determine our local IP addresses; detect internal IP & IPv6-support
617  *
618  * @param ah auto setup context
619  */
620 static void
621 test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
622 {
623   ah->have_v6 = GNUNET_NO;
624   ah->ret = GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO; // reset to success if any of the IFs in below iterator has a valid IP
625   GNUNET_OS_network_interfaces_list (&process_if, ah);
626
627   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
628                                          (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
629   next_phase (ah);
630 }
631
632
633 /**
634  * Test if NAT has been punched
635  *
636  * @param ah auto setup context
637  */
638 static void
639 test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
640 {
641   struct GNUNET_CLIENT_Connection *client;
642   struct GNUNET_NAT_TestMessage msg;
643
644   if (ah->stun_ip)
645   {
646     LOG (GNUNET_ERROR_TYPE_INFO,
647          "Asking gnunet-nat-server to connect to `%s'\n",
648          ah->stun_ip);
649
650
651     msg.header.size = htons (sizeof (struct GNUNET_NAT_TestMessage));
652     msg.header.type = htons (GNUNET_MESSAGE_TYPE_NAT_TEST);
653     msg.dst_ipv4 = inet_addr(ah->stun_ip);
654     msg.dport = htons(ah->stun_port);
655     msg.data = port;
656     msg.is_tcp = htonl ((uint32_t) GNUNET_NO);
657
658     client = GNUNET_CLIENT_connect ("gnunet-nat-server", ah->cfg);
659     if (NULL == client)
660     {
661       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
662                   _("Failed to connect to `gnunet-nat-server'\n"));
663       return;
664     }
665
666     GNUNET_break (GNUNET_OK ==
667                   GNUNET_CLIENT_transmit_and_get_response (client, &msg.header,
668                                                            NAT_SERVER_TIMEOUT,
669                                                            GNUNET_YES, NULL,
670                                                            NULL));
671     if (NULL != ltask4)
672     {
673       GNUNET_SCHEDULER_cancel (ltask4);
674       ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT,
675                                               lsock4,
676                                               &do_udp_read,
677                                               ah);
678     }
679   }
680   else
681   {
682     LOG (GNUNET_ERROR_TYPE_INFO,
683          "We don't have a STUN IP");
684     next_phase(ah);
685   }
686 }
687
688
689 /**
690  * Test if UPnPC works.
691  *
692  * @param ah auto setup context
693  */
694 static void
695 test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
696 {
697
698   int have_upnpc;
699
700   if (GNUNET_NAT_ERROR_SUCCESS != ah->ret)
701     next_phase (ah);
702
703   // test if upnpc is available
704   have_upnpc = (GNUNET_SYSERR !=
705                 GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
706   //FIXME: test if upnpc is actually working, that is, if transports start to work once we use UPnP
707   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
708               (have_upnpc)
709               ? _("upnpc found, enabling its use\n")
710               : _("upnpc not found\n"));
711   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
712                                          (GNUNET_YES == have_upnpc) ? "YES" : "NO");
713   next_phase (ah);
714
715 }
716
717
718 /**
719  * Test if ICMP server is working
720  *
721  * @param ah auto setup context
722  */
723 static void
724 test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
725 {
726
727   int ext_ip;
728   int nated;
729   int binary;
730   char *tmp;
731   char *helper;
732   ext_ip = GNUNET_NO;
733   nated = GNUNET_NO;
734   binary = GNUNET_NO;
735
736   tmp = NULL;
737   helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
738   if ((GNUNET_OK ==
739         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
740                                                &tmp)) && (0 < strlen (tmp))){
741     ext_ip = GNUNET_OK;
742     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_server not possible, as we have no public IPv4 address\n"));
743   }
744   else
745     goto err;
746
747   if (GNUNET_YES ==
748         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")){
749     nated = GNUNET_YES;
750     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_server not possible, as we are not behind NAT\n"));
751   }
752   else
753     goto err;
754
755   if (GNUNET_YES ==
756         GNUNET_OS_check_helper_binary (helper, GNUNET_YES, "-d 127.0.0.1" )){
757     binary = GNUNET_OK; // use localhost as source for that one udp-port, ok for testing
758     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("No working gnunet-helper-nat-server found\n"));
759   }
760 err:
761   GNUNET_free_non_null (tmp);
762   GNUNET_free (helper);
763
764   if (GNUNET_OK == ext_ip && GNUNET_YES == nated && GNUNET_OK == binary)
765     ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, ah);
766   else
767     next_phase (ah);
768 }
769
770
771 /**
772  * Test if ICMP client is working
773  *
774  * @param ah auto setup context
775  */
776 static void
777 test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
778 {
779   char *tmp;
780   char *helper;
781
782   tmp = NULL;
783   helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
784   if ((GNUNET_OK ==
785         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
786                                                &tmp)) && (0 < strlen (tmp)))
787   {
788     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_client not possible, as we have no internal IPv4 address\n"));
789   }
790   else
791     goto err;
792
793   if (GNUNET_YES !=
794       GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")){
795     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
796                 _("test_icmp_server not possible, as we are not behind NAT\n"));
797   }
798   else
799     goto err;
800
801   if (GNUNET_YES ==
802       GNUNET_OS_check_helper_binary (helper, GNUNET_YES, "-d 127.0.0.1 127.0.0.2 42")){
803           // none of these parameters are actually used in privilege testing mode
804     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
805                 _("No working gnunet-helper-nat-server found\n"));
806   }
807 err:
808   GNUNET_free_non_null (tmp);
809   GNUNET_free (helper);
810
811   next_phase (ah);
812 }
813
814
815 /**
816  * Run the next phase of the auto test.
817  */
818 static void
819 next_phase (struct GNUNET_NAT_AutoHandle *ah)
820 {
821   struct GNUNET_CONFIGURATION_Handle *diff;
822
823   ah->phase++;
824   switch (ah->phase)
825   {
826   case AUTO_INIT:
827     GNUNET_assert (0);
828     break;
829   case AUTO_EXTERNAL_IP:
830     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_EXTERNAL_IP\n");
831     test_external_ip (ah);
832     break;
833   case AUTO_STUN:
834     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_STUN\n");
835     test_stun (ah);
836     break;
837   case AUTO_LOCAL_IP:
838     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_LOCAL_IP\n");
839     test_local_ip (ah);
840     break;
841   case AUTO_NAT_PUNCHED:
842     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_NAT_PUNCHED\n");
843     test_nat_punched (ah);
844     break;
845   case AUTO_UPNPC:
846     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_UPNPC\n");
847     test_upnpc (ah);
848     break;
849   case AUTO_ICMP_SERVER:
850     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_ICMP_SERVER\n");
851     test_icmp_server (ah);
852     break;
853   case AUTO_ICMP_CLIENT:
854     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Will run AUTO_ICMP_CLIENT\n");
855     test_icmp_client (ah);
856     break;
857   case AUTO_DONE:
858     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Done with tests\n");
859     if (!ah->internal_ip_is_public)
860     {
861       GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
862
863       if (ah->connected_back)
864       {
865         GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "PUNCHED_NAT", "YES");
866       }
867       else
868       {
869         GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "PUNCHED_NAT", "NO");
870       }
871
872       if (ah->stun_ip)
873       {
874         GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
875                                                ah->stun_ip);
876         if (ah->connected_back)
877         {
878           ah->type = GNUNET_NAT_TYPE_STUN_PUNCHED_NAT;
879           GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "USE_STUN", "YES");
880         }
881         else
882         {
883           ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
884           GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "USE_STUN", "NO");
885         }
886
887       }
888       if (ah->stun_port)
889       {
890         GNUNET_CONFIGURATION_set_value_number (ah->cfg, "transport-udp",
891                                                "ADVERTISED_PORT",
892                                                ah->stun_port);
893       }
894
895     }
896     else
897     {
898       //The internal IP is the same as public, but we didn't got a incoming connection
899       if (ah->connected_back)
900       {
901         ah->type = GNUNET_NAT_TYPE_NO_NAT;
902         GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "NO");
903       }
904       else
905       {
906         GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
907         ah->type = GNUNET_NAT_TYPE_UNREACHABLE_NAT;
908         if (ah->stun_ip)
909         {
910           GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
911                                                  ah->stun_ip);
912         }
913         if (ah->stun_port)
914         {
915           GNUNET_CONFIGURATION_set_value_number (ah->cfg, "transport-udp",
916                                                  "ADVERTISED_PORT",
917                                                  ah->stun_port);
918
919         }
920       }
921     }
922
923     diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
924                                           ah->cfg);
925
926
927     ah->fin_cb (ah->fin_cb_cls,
928                 diff,
929                 ah->ret,
930                 ah->type);
931     GNUNET_CONFIGURATION_destroy (diff);
932     GNUNET_NAT_autoconfig_cancel (ah);
933     return;
934   }
935 }
936
937
938 /**
939  * Start auto-configuration routine.  The resolver service should
940  * be available when this function is called.
941  *
942  * @param cfg initial configuration
943  * @param cb function to call with autoconfiguration result
944  * @param cb_cls closure for @a cb
945  * @return handle to cancel operation
946  */
947 struct GNUNET_NAT_AutoHandle *
948 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
949                              GNUNET_NAT_AutoResultCallback cb,
950                              void *cb_cls)
951 {
952   struct GNUNET_NAT_AutoHandle *ah;
953
954   ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
955   ah->fin_cb = cb;
956   ah->fin_cb_cls = cb_cls;
957   ah->ret = GNUNET_NAT_ERROR_SUCCESS;
958   ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
959   ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
960
961   /* never use loopback addresses if user wanted autoconfiguration */
962   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
963                                          "USE_LOCALADDR",
964                                          "NO");
965
966   next_phase (ah);
967   return ah;
968 }
969
970
971 /**
972  * Abort autoconfiguration.
973  *
974  * @param ah handle for operation to abort
975  */
976 void
977 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
978 {
979   if (NULL != ah->tst)
980   {
981     GNUNET_NAT_test_stop (ah->tst);
982     ah->tst = NULL;
983   }
984   if (NULL != ah->eh)
985   {
986     GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
987     ah->eh = NULL;
988   }
989   if (NULL != ah->task)
990   {
991     GNUNET_SCHEDULER_cancel (ah->task);
992     ah->task = NULL;
993   }
994   GNUNET_CONFIGURATION_destroy (ah->cfg);
995   GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
996   GNUNET_free (ah);
997 }
998
999
1000 /* end of nat_auto.c */