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