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