move external IP logic to separate file
[oweals/gnunet.git] / src / nat / gnunet-service-nat.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2016, 2017 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/gnunet-service-nat.c
23  * @brief network address translation traversal service
24  * @author Christian Grothoff
25  *
26  * The purpose of this service is to enable transports to 
27  * traverse NAT routers, by providing traversal options and
28  * knowledge about the local network topology.
29  *
30  * TODO:
31  * - test and document (!) ICMP based NAT traversal
32  * - implement manual hole punching support (AUTO missing)
33  * - test manual hole punching support
34  * - implement "more" autoconfig:
35  *   + consider moving autoconfig-logic into separate service! 
36  *   + re-work gnunet-nat-server & integrate!
37  *   + test manually punched NAT (how?)
38  * - implement & test STUN processing to classify NAT;
39  *   basically, open port & try different methods.
40  */
41 #include "platform.h"
42 #include <math.h>
43 #include "gnunet_util_lib.h"
44 #include "gnunet_protocols.h"
45 #include "gnunet_signatures.h"
46 #include "gnunet_statistics_service.h"
47 #include "gnunet_resolver_service.h"
48 #include "gnunet_nat_service.h"
49 #include "gnunet-service-nat.h"
50 #include "gnunet-service-nat_externalip.h"
51 #include "gnunet-service-nat_stun.h"
52 #include "gnunet-service-nat_mini.h"
53 #include "gnunet-service-nat_helper.h"
54 #include "nat.h"
55 #include <gcrypt.h>
56
57
58 /**
59  * How often should we ask the OS about a list of active
60  * network interfaces?
61  */
62 #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
63
64 /**
65  * How long do we wait until we forcefully terminate autoconfiguration?
66  */
67 #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
68
69 /**
70  * How often do we scan for changes in how our external (dyndns) hostname resolves?
71  */
72 #define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 7)
73
74
75 /**
76  * Information we track per client address. 
77  */
78 struct ClientAddress
79 {
80   /**
81    * Network address used by the client.
82    */
83   struct sockaddr_storage ss;
84
85   /**
86    * Handle to active UPnP request where we asked upnpc to open
87    * a port at the NAT.  NULL if we do not have such a request
88    * pending.
89    */
90   struct GNUNET_NAT_MiniHandle *mh;
91   
92 };
93
94
95 /**
96  * List of local addresses this system has.
97  */
98 struct LocalAddressList
99 {
100   /**
101    * This is a linked list.
102    */
103   struct LocalAddressList *next;
104
105   /**
106    * Previous entry.
107    */
108   struct LocalAddressList *prev;
109
110   /**
111    * Context for a gnunet-helper-nat-server used to listen
112    * for ICMP messages to this client for connection reversal.
113    */
114   struct HelperContext *hc;
115   
116   /**
117    * The address itself (i.e. `struct sockaddr_in` or `struct
118    * sockaddr_in6`, in the respective byte order).
119    */
120   struct sockaddr_storage addr;
121
122   /**
123    * Address family. (FIXME: redundant, addr.ss_family! Remove!?)
124    */
125   int af;
126   
127   /**
128    * #GNUNET_YES if we saw this one in the previous iteration,
129    * but not in the current iteration and thus might need to
130    * remove it at the end.
131    */
132   int old;
133
134   /**
135    * What type of address is this?
136    */
137   enum GNUNET_NAT_AddressClass ac;
138   
139 };
140
141
142 /**
143  * Internal data structure we track for each of our clients.
144  */
145 struct ClientHandle
146 {
147
148   /**
149    * Kept in a DLL.
150    */
151   struct ClientHandle *next;
152   
153   /**
154    * Kept in a DLL.
155    */
156   struct ClientHandle *prev;
157
158   /**
159    * Underlying handle for this client with the service.
160    */ 
161   struct GNUNET_SERVICE_Client *client;
162
163   /**
164    * Message queue for communicating with the client.
165    */
166   struct GNUNET_MQ_Handle *mq;
167
168   /**
169    * Array of addresses used by the service.
170    */
171   struct ClientAddress *caddrs;
172
173   /**
174    * External DNS name and port given by user due to manual
175    * hole punching.  Special DNS name 'AUTO' is used to indicate
176    * desire for automatic determination of the external IP 
177    * (instead of DNS or manual configuration, i.e. to be used 
178    * if the IP keeps changing and we have no DynDNS, but we do
179    * have a hole punched).
180    */
181   char *hole_external;
182
183   /**
184    * Task for periodically re-running the @e ext_dns DNS lookup.
185    */ 
186   struct GNUNET_SCHEDULER_Task *ext_dns_task;
187   
188   /**
189    * Handle for (DYN)DNS lookup of our external IP as given in
190    * @e hole_external.
191    */
192   struct GNUNET_RESOLVER_RequestHandle *ext_dns;
193
194   /**
195    * Handle for monitoring external IP changes.
196    */
197   struct GN_ExternalIPMonitor *external_monitor;
198   
199   /**
200    * DLL of external IP addresses as given in @e hole_external.
201    */
202   struct LocalAddressList *ext_addr_head;
203
204   /**
205    * DLL of external IP addresses as given in @e hole_external.
206    */
207   struct LocalAddressList *ext_addr_tail;
208
209   /**
210    * Port number we found in @e hole_external.
211    */ 
212   uint16_t ext_dns_port;
213   
214   /**
215    * What does this client care about?
216    */
217   enum GNUNET_NAT_RegisterFlags flags;
218
219   /**
220    * Is any of the @e caddrs in a reserved subnet for NAT?
221    */
222   int natted_address;
223   
224   /**
225    * Number of addresses that this service is bound to.
226    * Length of the @e caddrs array.
227    */
228   uint16_t num_caddrs;
229   
230   /**
231    * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
232    */
233   uint8_t proto;
234
235 };
236
237
238 /**
239  * External IP address as given to us via some STUN server.
240  */
241 struct StunExternalIP
242 {
243   /**
244    * Kept in a DLL.
245    */ 
246   struct StunExternalIP *next;
247
248   /**
249    * Kept in a DLL.
250    */ 
251   struct StunExternalIP *prev;
252
253   /**
254    * Task we run to remove this entry when it is stale.
255    */
256   struct GNUNET_SCHEDULER_Task *timeout_task;
257
258   /**
259    * Our external IP address as reported by the 
260    * STUN server.
261    */
262   struct sockaddr_in external_addr;
263
264   /**
265    * Address of the reporting STUN server.  Used to 
266    * detect when a STUN server changes its opinion
267    * to more quickly remove stale results.
268    */
269   struct sockaddr_storage stun_server_addr;
270
271   /**
272    * Number of bytes used in @e stun_server_addr.
273    */
274   size_t stun_server_addr_len;
275 };
276
277
278 /**
279  * Context for autoconfiguration operations.
280  */
281 struct AutoconfigContext
282 {
283   /**
284    * Kept in a DLL.
285    */
286   struct AutoconfigContext *prev;
287
288   /**
289    * Kept in a DLL.
290    */
291   struct AutoconfigContext *next;
292
293   /**
294    * Which client asked the question.
295    */
296   struct ClientHandle *ch;
297
298   /**
299    * Configuration we are creating.
300    */ 
301   struct GNUNET_CONFIGURATION_Handle *c;
302
303   /**
304    * Original configuration (for diffing).
305    */ 
306   struct GNUNET_CONFIGURATION_Handle *orig;
307
308   /**
309    * Timeout task to force termination.
310    */
311   struct GNUNET_SCHEDULER_Task *timeout_task;
312
313   /**
314    * What type of system are we on?
315    */
316   char *system_type;
317
318   /**
319    * Handle to activity to probe for our external IP.
320    */
321   struct GNUNET_NAT_ExternalHandle *probe_external;
322
323   /**
324    * #GNUNET_YES if upnpc should be used,
325    * #GNUNET_NO if upnpc should not be used,
326    * #GNUNET_SYSERR if we should simply not change the option.
327    */
328   int enable_upnpc;
329
330   /**
331    * Status code to return to the client.
332    */
333   enum GNUNET_NAT_StatusCode status_code;
334
335   /**
336    * NAT type to return to the client.
337    */
338   enum GNUNET_NAT_Type type;
339 };
340
341
342 /**
343  * DLL of our autoconfiguration operations.
344  */
345 static struct AutoconfigContext *ac_head;
346
347 /**
348  * DLL of our autoconfiguration operations.
349  */
350 static struct AutoconfigContext *ac_tail;
351
352 /**
353  * Timeout to use when STUN data is considered stale.
354  */
355 static struct GNUNET_TIME_Relative stun_stale_timeout;
356
357 /**
358  * How often do we scan for changes in how our external (dyndns) hostname resolves?
359  */
360 static struct GNUNET_TIME_Relative dyndns_frequency;
361
362 /**
363  * Handle to our current configuration.
364  */
365 static const struct GNUNET_CONFIGURATION_Handle *cfg;
366
367 /**
368  * Handle to the statistics service.
369  */
370 static struct GNUNET_STATISTICS_Handle *stats;
371
372 /**
373  * Task scheduled to periodically scan our network interfaces.
374  */
375 static struct GNUNET_SCHEDULER_Task *scan_task;
376
377 /**
378  * Head of client DLL.
379  */
380 static struct ClientHandle *ch_head;
381   
382 /**
383  * Tail of client DLL.
384  */
385 static struct ClientHandle *ch_tail;
386
387 /**
388  * Head of DLL of local addresses.
389  */
390 static struct LocalAddressList *lal_head;
391
392 /**
393  * Tail of DLL of local addresses.
394  */
395 static struct LocalAddressList *lal_tail;
396
397 /**
398  * Kept in a DLL.
399  */ 
400 static struct StunExternalIP *se_head;
401
402 /**
403  * Kept in a DLL.
404  */ 
405 static struct StunExternalIP *se_tail;
406
407 /**
408  * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
409  * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
410  */
411 int enable_upnp;
412
413
414 /**
415  * Remove and free an entry from the #lal_head DLL.
416  *
417  * @param lal entry to free
418  */
419 static void
420 free_lal (struct LocalAddressList *lal)
421 {
422   GNUNET_CONTAINER_DLL_remove (lal_head,
423                                lal_tail,
424                                lal);
425   if (NULL != lal->hc)
426   {
427     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
428                 "Lost NATed local address %s, stopping NAT server\n",
429                 GNUNET_a2s ((const struct sockaddr *) &lal->addr,
430                             sizeof (struct sockaddr_in)));
431
432     GN_stop_gnunet_nat_server_ (lal->hc);
433     lal->hc = NULL;
434   }
435   GNUNET_free (lal);
436 }
437
438
439 /**
440  * Free the DLL starting at #lal_head.
441  */ 
442 static void
443 destroy_lal ()
444 {
445   struct LocalAddressList *lal;
446
447   while (NULL != (lal = lal_head))
448     free_lal (lal);
449 }
450
451
452 /**
453  * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from
454  * client.
455  *
456  * @param cls client who sent the message
457  * @param message the message received
458  * @return #GNUNET_OK if message is well-formed
459  */
460 static int
461 check_register (void *cls,
462                 const struct GNUNET_NAT_RegisterMessage *message)
463 {
464   uint16_t num_addrs = ntohs (message->num_addrs);
465   const char *off = (const char *) &message[1];
466   size_t left = ntohs (message->header.size) - sizeof (*message);
467
468   for (unsigned int i=0;i<num_addrs;i++)
469   {
470     size_t alen;
471     const struct sockaddr *sa = (const struct sockaddr *) off;
472
473     if (sizeof (sa_family_t) > left)
474     {
475       GNUNET_break (0);
476       return GNUNET_SYSERR;
477     }
478     switch (sa->sa_family)
479     {
480     case AF_INET:
481       alen = sizeof (struct sockaddr_in);
482       break;
483     case AF_INET6:
484       alen = sizeof (struct sockaddr_in6);
485       break;
486 #if AF_UNIX
487     case AF_UNIX:
488       alen = sizeof (struct sockaddr_un);
489       break;
490 #endif
491     default:
492       GNUNET_break (0);
493       return GNUNET_SYSERR;      
494     }
495     if (alen > left)
496     {
497       GNUNET_break (0);
498       return GNUNET_SYSERR;      
499     }
500     off += alen;
501     left -= alen;
502   }
503   if (left != ntohs (message->hole_external_len))
504   {
505     GNUNET_break (0);
506     return GNUNET_SYSERR;      
507   }
508   return GNUNET_OK; 
509 }
510
511
512 /**
513  * Check if @a ip is in @a network with @a bits netmask.
514  *
515  * @param network to test
516  * @param ip IP address to test
517  * @param bits bitmask for the network
518  * @return #GNUNET_YES if @a ip is in @a network
519  */
520 static int
521 match_ipv4 (const char *network,
522             const struct in_addr *ip,
523             uint8_t bits)
524 {
525   struct in_addr net;
526
527   if (0 == ip->s_addr)
528     return GNUNET_YES;
529   if (0 == bits)
530     return GNUNET_YES;
531   GNUNET_assert (1 == inet_pton (AF_INET,
532                                  network,
533                                  &net));
534   return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits)));
535 }
536
537
538 /**
539  * Check if @a ip is in @a network with @a bits netmask.
540  *
541  * @param network to test
542  * @param ip IP address to test
543  * @param bits bitmask for the network
544  * @return #GNUNET_YES if @a ip is in @a network
545  */
546 static int
547 match_ipv6 (const char *network,
548             const struct in6_addr *ip,
549             uint8_t bits)
550 {
551   struct in6_addr net;
552   struct in6_addr mask;
553   unsigned int off;
554   
555   if (0 == bits)
556     return GNUNET_YES;
557   GNUNET_assert (1 == inet_pton (AF_INET6,
558                                  network,
559                                  &net));
560   memset (&mask, 0, sizeof (mask));
561   if (0 == memcmp (&mask,
562                    ip,
563                    sizeof (mask)))
564     return GNUNET_YES;
565   off = 0;
566   while (bits > 8)
567   {
568     mask.s6_addr[off++] = 0xFF;
569     bits -= 8;
570   }
571   while (bits > 0)
572   {
573     mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80;
574     bits--;
575   }
576   for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++)
577     if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) !=
578         (((uint32_t *) &net)[j] & ((int *) &mask)[j]))
579       return GNUNET_NO;
580   return GNUNET_YES;
581 }
582
583
584 /**
585  * Test if the given IPv4 address is in a known range
586  * for private networks.
587  *
588  * @param ip address to test
589  * @return #GNUNET_YES if @a ip is in a NAT range
590  */
591 static int
592 is_nat_v4 (const struct in_addr *ip)
593 {
594   return
595     match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */
596     match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */
597     match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */
598     match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */
599     match_ipv4 ("172.16.0.0", ip, 16);  /* RFC 1918 */
600 }
601
602
603 /**
604  * Test if the given IPv6 address is in a known range
605  * for private networks.
606  *
607  * @param ip address to test
608  * @return #GNUNET_YES if @a ip is in a NAT range
609  */
610 static int
611 is_nat_v6 (const struct in6_addr *ip)
612 {
613   return
614     match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */
615     match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */
616     match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */
617 }
618
619
620 /**
621  * Closure for #ifc_proc.
622  */
623 struct IfcProcContext
624 {
625
626   /** 
627    * Head of DLL of local addresses.
628    */
629   struct LocalAddressList *lal_head;
630
631   /**
632    * Tail of DLL of local addresses.
633    */
634   struct LocalAddressList *lal_tail;
635
636 };
637
638
639 /**
640  * Callback function invoked for each interface found.  Adds them
641  * to our new address list.
642  *
643  * @param cls a `struct IfcProcContext *`
644  * @param name name of the interface (can be NULL for unknown)
645  * @param isDefault is this presumably the default interface
646  * @param addr address of this interface (can be NULL for unknown or unassigned)
647  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
648  * @param netmask the network mask (can be NULL for unknown or unassigned)
649  * @param addrlen length of the address
650  * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
651  */
652 static int
653 ifc_proc (void *cls,
654           const char *name,
655           int isDefault,
656           const struct sockaddr *addr,
657           const struct sockaddr *broadcast_addr,
658           const struct sockaddr *netmask,
659           socklen_t addrlen)
660 {
661   struct IfcProcContext *ifc_ctx = cls;
662   struct LocalAddressList *lal;
663   size_t alen;
664   const struct in_addr *ip4;
665   const struct in6_addr *ip6;
666   enum GNUNET_NAT_AddressClass ac;
667
668   switch (addr->sa_family)
669   {
670   case AF_INET:
671     alen = sizeof (struct sockaddr_in);
672     ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
673     if (match_ipv4 ("127.0.0.0", ip4, 8))
674       ac = GNUNET_NAT_AC_LOOPBACK;
675     else if (is_nat_v4 (ip4))
676       ac = GNUNET_NAT_AC_LAN;
677     else
678       ac = GNUNET_NAT_AC_GLOBAL;
679     break;
680   case AF_INET6:
681     alen = sizeof (struct sockaddr_in6);
682     ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
683     if (match_ipv6 ("::1", ip6, 128))
684       ac = GNUNET_NAT_AC_LOOPBACK;
685     else if (is_nat_v6 (ip6))
686       ac = GNUNET_NAT_AC_LAN;
687     else
688       ac = GNUNET_NAT_AC_GLOBAL;
689     if ( (ip6->s6_addr[11] == 0xFF) &&
690          (ip6->s6_addr[12] == 0xFE) )
691     {
692       /* contains a MAC, be extra careful! */
693       ac |= GNUNET_NAT_AC_PRIVATE;
694     }
695     break;
696 #if AF_UNIX
697   case AF_UNIX:
698     GNUNET_break (0);
699     return GNUNET_OK;
700 #endif
701   default:
702     GNUNET_break (0);
703     return GNUNET_OK;
704   }
705   lal = GNUNET_malloc (sizeof (*lal));
706   lal->af = addr->sa_family;
707   lal->ac = ac;
708   GNUNET_memcpy (&lal->addr,
709                  addr,
710                  alen);
711   GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
712                                ifc_ctx->lal_tail,
713                                lal);
714   return GNUNET_OK;
715 }
716
717
718 /**
719  * Notify client about a change in the list of addresses this peer
720  * has.
721  *
722  * @param ac address class of the entry in the list that changed
723  * @param ch client to contact
724  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
725  * @param addr the address that changed
726  * @param addr_len number of bytes in @a addr
727  */
728 static void
729 notify_client (enum GNUNET_NAT_AddressClass ac,
730                struct ClientHandle *ch,
731                int add,
732                const void *addr,
733                size_t addr_len)
734 {
735   struct GNUNET_MQ_Envelope *env;
736   struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
737   
738   env = GNUNET_MQ_msg_extra (msg,
739                              addr_len,
740                              GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
741   msg->add_remove = htonl (add);
742   msg->addr_class = htonl (ac);
743   GNUNET_memcpy (&msg[1],
744                  addr,
745                  addr_len);
746   GNUNET_MQ_send (ch->mq,
747                   env);
748 }                      
749
750
751 /**
752  * Check if we should bother to notify this client about this
753  * address change, and if so, do it.
754  *
755  * @param delta the entry in the list that changed
756  * @param ch client to check
757  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
758  */
759 static void
760 check_notify_client (struct LocalAddressList *delta,
761                      struct ClientHandle *ch,
762                      int add)
763 {
764   size_t alen;
765   struct sockaddr_in v4;
766   struct sockaddr_in6 v6;
767   
768   if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
769     return;
770   switch (delta->af)
771   {
772   case AF_INET:
773     alen = sizeof (struct sockaddr_in);
774     GNUNET_memcpy (&v4,
775                    &delta->addr,
776                    alen);
777     
778     /* Check for client notifications */
779     for (unsigned int i=0;i<ch->num_caddrs;i++)
780     {
781       const struct sockaddr_in *c4;
782
783       if (AF_INET != ch->caddrs[i].ss.ss_family)
784         continue; /* IPv4 not relevant */
785       c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss;
786       if ( match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) &&
787            (0 != c4->sin_addr.s_addr) &&
788            (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) )
789         continue; /* bound to loopback, but this is not loopback */
790       if ( (! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) ) &&
791            (0 != c4->sin_addr.s_addr) &&
792            match_ipv4 ("127.0.0.1", &v4.sin_addr, 8) )
793         continue; /* bound to non-loopback, but this is loopback */
794       if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
795            (0 != c4->sin_addr.s_addr) &&
796            (! is_nat_v4 (&v4.sin_addr)) )
797         continue; /* based on external-IP, but this IP is not
798                      from private address range. */
799       if ( (0 != memcmp (&v4.sin_addr,
800                          &c4->sin_addr,
801                          sizeof (struct in_addr))) &&
802            (0 != c4->sin_addr.s_addr) &&
803            ( (! is_nat_v4 (&c4->sin_addr)) ||
804              (0 == (ch->flags & GNUNET_NAT_AC_EXTERN))) )
805         continue; /* this IP is not from private address range,
806                      and IP does not match. */
807
808       /* OK, IP seems relevant, notify client */
809       v4.sin_port = c4->sin_port;
810       notify_client (delta->ac,
811                      ch,
812                      add,
813                      &v4,
814                      alen);
815     }
816     break;
817   case AF_INET6:
818     alen = sizeof (struct sockaddr_in6);
819     GNUNET_memcpy (&v6,
820                    &delta->addr,
821                    alen);
822     for (unsigned int i=0;i<ch->num_caddrs;i++)
823     {
824       const struct sockaddr_in6 *c6;
825       
826       if (AF_INET6 != ch->caddrs[i].ss.ss_family)
827         continue; /* IPv4 not relevant */
828       c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss;
829       if ( match_ipv6 ("::1", &c6->sin6_addr, 128) &&
830            (0 != memcmp (&c6->sin6_addr,
831                          &in6addr_any,
832                          sizeof (struct in6_addr))) &&
833            (! match_ipv6 ("::1", &v6.sin6_addr, 128)) )
834         continue; /* bound to loopback, but this is not loopback */
835       if ( (! match_ipv6 ("::1", &c6->sin6_addr, 128) ) &&
836            (0 != memcmp (&c6->sin6_addr,
837                          &in6addr_any,
838                          sizeof (struct in6_addr))) &&
839            match_ipv6 ("::1", &v6.sin6_addr, 128) )
840         continue; /* bound to non-loopback, but this is loopback */
841       if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
842            (0 != memcmp (&c6->sin6_addr,
843                          &in6addr_any,
844                          sizeof (struct in6_addr))) &&
845            (! is_nat_v6 (&v6.sin6_addr)) )
846         continue; /* based on external-IP, but this IP is not
847                      from private address range. */
848       if ( (0 != memcmp (&v6.sin6_addr,
849                          &c6->sin6_addr,
850                          sizeof (struct in6_addr))) &&
851            (0 != memcmp (&c6->sin6_addr,
852                          &in6addr_any,
853                          sizeof (struct in6_addr))) &&
854            (! is_nat_v6 (&c6->sin6_addr)) )
855         continue; /* this IP is not from private address range,
856                      and IP does not match. */
857       if ( (match_ipv6 ("fe80::", &c6->sin6_addr, 10)) &&
858            (0 != memcmp (&c6->sin6_addr,
859                          &in6addr_any,
860                          sizeof (struct in6_addr))) &&
861            (0 != memcmp (&v6.sin6_addr,
862                          &c6->sin6_addr,
863                          sizeof (struct in6_addr))) &&
864            (0 == (delta->ac & GNUNET_NAT_AC_EXTERN)) )
865         continue; /* client bound to link-local, and the other address
866                      does not match and is not an external IP */
867
868       /* OK, IP seems relevant, notify client */
869       v6.sin6_port = c6->sin6_port;
870       notify_client (delta->ac,
871                      ch,
872                      add,
873                      &v6,
874                      alen);
875     }
876     break;
877   default:
878     GNUNET_break (0);
879     return;
880   }
881 }
882
883
884 /**
885  * Notify all clients about a change in the list
886  * of addresses this peer has.
887  *
888  * @param delta the entry in the list that changed
889  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
890  */
891 static void
892 notify_clients (struct LocalAddressList *delta,
893                 int add)
894 {
895   for (struct ClientHandle *ch = ch_head;
896        NULL != ch;
897        ch = ch->next)
898     check_notify_client (delta,
899                          ch,
900                          add);
901 }
902
903
904 /**
905  * Tell relevant client about a change in our external
906  * IPv4 address.
907  * 
908  * @param cls client to check if it cares and possibly notify
909  * @param v4 the external address that changed
910  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
911  */
912 static void
913 notify_client_external_ipv4_change (void *cls,
914                                     const struct in_addr *v4,
915                                     int add)
916 {
917   struct ClientHandle *ch = cls;
918   struct sockaddr_in sa;
919   int have_v4;
920
921   /* (1) check if client cares. */
922   if (! ch->natted_address)
923     return;
924   if (0 == (GNUNET_NAT_RF_ADDRESSES & ch->flags))
925     return;
926   have_v4 = GNUNET_NO;
927   for (unsigned int i=0;i<ch->num_caddrs;i++)
928   {
929     const struct sockaddr_storage *ss = &ch->caddrs[i].ss;
930
931     if (AF_INET != ss->ss_family)
932       continue;
933     have_v4 = GNUNET_YES;
934     break;
935   }
936   if (GNUNET_NO == have_v4)
937     return; /* IPv6-only */
938
939   /* build address info */
940   memset (&sa,
941           0,
942           sizeof (sa));
943   sa.sin_family = AF_INET;
944   sa.sin_addr = *v4;
945   sa.sin_port = htons (0);
946   
947   /* (3) notify client of change */
948   notify_client (is_nat_v4 (v4)
949                  ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN 
950                  : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL,
951                  ch,
952                  add,
953                  &sa,
954                  sizeof (sa));
955 }
956
957
958 /**
959  * We got a connection reversal request from another peer.
960  * Notify applicable clients.
961  *
962  * @param cls closure with the `struct LocalAddressList` 
963  * @param ra IP address of the peer who wants us to connect to it 
964  */
965 static void
966 reversal_callback (void *cls,
967                    const struct sockaddr_in *ra)
968 {
969   struct LocalAddressList *lal = cls;
970   const struct sockaddr_in *l4;
971
972   GNUNET_assert (AF_INET == lal->af);
973   l4 = (const struct sockaddr_in *) &lal->addr;
974   for (struct ClientHandle *ch = ch_head;
975        NULL != ch;
976        ch = ch->next)
977   {    
978     struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm;
979     struct GNUNET_MQ_Envelope *env;
980     int match;
981
982     /* Check if client is in applicable range for ICMP NAT traversal
983        for this local address */
984     if (! ch->natted_address)
985       continue;
986     match = GNUNET_NO;
987     for (unsigned int i=0;i<ch->num_caddrs;i++)
988     {
989       struct ClientAddress *ca = &ch->caddrs[i];
990       const struct sockaddr_in *c4;
991       
992       if (AF_INET != ca->ss.ss_family)
993         continue;
994       c4 = (const struct sockaddr_in *) &ca->ss;
995       if ( (0 != c4->sin_addr.s_addr) &&
996            (l4->sin_addr.s_addr != c4->sin_addr.s_addr) )
997         continue;
998       match = GNUNET_YES;
999       break;
1000     }
1001     if (! match)
1002       continue;
1003
1004     /* Notify applicable client about connection reversal request */
1005     env = GNUNET_MQ_msg_extra (crrm,
1006                                sizeof (struct sockaddr_in),
1007                                GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED);
1008     GNUNET_memcpy (&crrm[1],
1009                    ra,
1010                    sizeof (struct sockaddr_in));
1011     GNUNET_MQ_send (ch->mq,
1012                     env);
1013   }
1014 }
1015
1016
1017 /**
1018  * Task we run periodically to scan for network interfaces.
1019  *
1020  * @param cls NULL
1021  */ 
1022 static void
1023 run_scan (void *cls)
1024 {
1025   struct IfcProcContext ifc_ctx;
1026   int found;
1027   int have_nat;
1028   struct LocalAddressList *lnext;
1029   
1030   scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
1031                                             &run_scan,
1032                                             NULL);
1033   memset (&ifc_ctx,
1034           0,
1035           sizeof (ifc_ctx));
1036   GNUNET_OS_network_interfaces_list (&ifc_proc,
1037                                      &ifc_ctx);
1038   /* remove addresses that disappeared */
1039   for (struct LocalAddressList *lal = lal_head;
1040        NULL != lal;
1041        lal = lnext)
1042   {
1043     lnext = lal->next;
1044     found = GNUNET_NO;
1045     for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1046          NULL != pos;
1047          pos = pos->next)
1048     {
1049       if ( (pos->af == lal->af) &&
1050            (0 == memcmp (&lal->addr,
1051                          &pos->addr,
1052                          (AF_INET == lal->af)
1053                          ? sizeof (struct sockaddr_in)
1054                          : sizeof (struct sockaddr_in6))) )
1055       {
1056         found = GNUNET_YES;
1057       }
1058     }
1059     if (GNUNET_NO == found)
1060     {
1061       notify_clients (lal,
1062                       GNUNET_NO);
1063       free_lal (lal);
1064     }
1065   }
1066
1067   /* add addresses that appeared */
1068   have_nat = GNUNET_NO;
1069   for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1070        NULL != pos;
1071        pos = ifc_ctx.lal_head)
1072   {
1073     found = GNUNET_NO;
1074     if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac))
1075       have_nat = GNUNET_YES;
1076     for (struct LocalAddressList *lal = lal_head;
1077          NULL != lal;
1078          lal = lal->next)
1079     {
1080       if ( (pos->af == lal->af) &&
1081            (0 == memcmp (&lal->addr,
1082                          &pos->addr,
1083                          (AF_INET == lal->af)
1084                          ? sizeof (struct sockaddr_in)
1085                          : sizeof (struct sockaddr_in6))) )
1086         found = GNUNET_YES;
1087     }
1088     GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head,
1089                                  ifc_ctx.lal_tail,
1090                                  pos);
1091     if (GNUNET_YES == found)
1092     {
1093       GNUNET_free (pos);
1094     }
1095     else
1096     {
1097       notify_clients (pos,
1098                       GNUNET_YES);
1099       GNUNET_CONTAINER_DLL_insert (lal_head,
1100                                    lal_tail,
1101                                    pos);
1102       if ( (AF_INET == pos->af) &&
1103            (NULL == pos->hc) &&
1104            (0 != (GNUNET_NAT_AC_LAN & pos->ac)) )
1105       {
1106         const struct sockaddr_in *s4
1107           = (const struct sockaddr_in *) &pos->addr;
1108         
1109         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1110                     "Found NATed local address %s, starting NAT server\n",
1111                     GNUNET_a2s ((void *) &pos->addr, sizeof (*s4)));
1112         pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr,
1113                                                &reversal_callback,
1114                                                pos);
1115       }
1116     }
1117   }
1118   GN_nat_status_changed (have_nat);
1119 }
1120
1121
1122 /**
1123  * Function called whenever our set of external addresses
1124  * as created by `upnpc` changes.
1125  *
1126  * @param cls closure with our `struct ClientHandle *`
1127  * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
1128  *     the previous (now invalid) one, #GNUNET_SYSERR indicates an error
1129  * @param addr either the previous or the new public IP address
1130  * @param addrlen actual length of the @a addr
1131  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
1132  */
1133 static void
1134 upnp_addr_change_cb (void *cls,
1135                      int add_remove,
1136                      const struct sockaddr *addr,
1137                      socklen_t addrlen,
1138                      enum GNUNET_NAT_StatusCode result)
1139 {
1140   struct ClientHandle *ch = cls;
1141   enum GNUNET_NAT_AddressClass ac;
1142
1143   switch (result)
1144   {
1145   case GNUNET_NAT_ERROR_SUCCESS:
1146     GNUNET_assert (NULL != addr);
1147     break;
1148   case GNUNET_NAT_ERROR_UPNPC_FAILED:
1149   case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
1150   case GNUNET_NAT_ERROR_IPC_FAILURE:
1151     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152                 "Running upnpc failed: %d\n",
1153                 result);
1154     return;
1155   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
1156     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1157                 "external-ip binary not found\n");
1158     return;
1159   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
1160     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1161                 "external-ip binary could not be run\n");
1162     return;
1163   case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
1164     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1165                 "upnpc failed to create port mapping\n");
1166     return;
1167   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1168     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169                 "Invalid output from upnpc\n");
1170     return;
1171   case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1172     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1173                 "Invalid address returned by upnpc\n");
1174     return;
1175   default:
1176     GNUNET_break (0); /* should not be possible */
1177     return;
1178   }
1179   switch (addr->sa_family)
1180   {
1181   case AF_INET:
1182     ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr)
1183       ? GNUNET_NAT_AC_LAN
1184       : GNUNET_NAT_AC_EXTERN;
1185     break;
1186   case AF_INET6:
1187     ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr)
1188       ? GNUNET_NAT_AC_LAN
1189       : GNUNET_NAT_AC_EXTERN;
1190     break;
1191   default:
1192     GNUNET_break (0);
1193     return;
1194   }
1195   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1196               "upnp external address %s: %s\n",
1197               add_remove ? "added" : "removed",
1198               GNUNET_a2s (addr,
1199                           addrlen));
1200   notify_client (ac,
1201                  ch,
1202                  add_remove,
1203                  addr,
1204                  addrlen);
1205 }
1206
1207
1208 /**
1209  * Resolve the `hole_external` name to figure out our 
1210  * external address from a manually punched hole.  The
1211  * port number has already been parsed, this task is 
1212  * responsible for periodically doing a DNS lookup.
1213  *
1214  * @param ch client handle to act upon
1215  */
1216 static void
1217 dyndns_lookup (void *cls);
1218
1219
1220 /**
1221  * Our (external) hostname was resolved.  Update lists of
1222  * current external IPs (note that DNS may return multiple
1223  * addresses!) and notify client accordingly.
1224  *
1225  * @param cls the `struct ClientHandle`
1226  * @param addr NULL on error, otherwise result of DNS lookup
1227  * @param addrlen number of bytes in @a addr
1228  */
1229 static void
1230 process_external_ip (void *cls,
1231                      const struct sockaddr *addr,
1232                      socklen_t addrlen)
1233 {
1234   struct ClientHandle *ch = cls;
1235   struct LocalAddressList *lal;
1236   struct sockaddr_storage ss;
1237   struct sockaddr_in *v4;
1238   struct sockaddr_in6 *v6;
1239
1240   if (NULL == addr)
1241   {
1242     struct LocalAddressList *laln;
1243     
1244     ch->ext_dns = NULL;
1245     ch->ext_dns_task 
1246       = GNUNET_SCHEDULER_add_delayed (dyndns_frequency,
1247                                       &dyndns_lookup,
1248                                       ch);
1249     /* Current iteration is over, remove 'old' IPs now */
1250     for (lal = ch->ext_addr_head; NULL != lal; lal = laln)
1251     {
1252       laln = lal->next;
1253       if (GNUNET_YES == lal->old)
1254       {
1255         GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
1256                                      ch->ext_addr_tail,
1257                                      lal);
1258         check_notify_client (lal,
1259                              ch,
1260                              GNUNET_NO);
1261         GNUNET_free (lal);
1262       }
1263     }
1264     return;
1265   }
1266   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1267               "Got IP `%s' for external address `%s'\n",
1268               GNUNET_a2s (addr,
1269                           addrlen),
1270               ch->hole_external);
1271   
1272   /* build sockaddr storage with port number */
1273   memset (&ss, 0, sizeof (ss));
1274   memcpy (&ss, addr, addrlen);
1275   switch (addr->sa_family)
1276   {
1277   case AF_INET:
1278     v4 = (struct sockaddr_in *) &ss;
1279     v4->sin_port = htons (ch->ext_dns_port);
1280     break;
1281   case AF_INET6:
1282     v6 = (struct sockaddr_in6 *) &ss;
1283     v6->sin6_port = htons (ch->ext_dns_port);
1284     break;
1285   default:
1286     GNUNET_break (0);
1287     return;
1288   }
1289   /* See if 'ss' matches any of our known addresses */
1290   for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
1291   {
1292     if (GNUNET_NO == lal->old)
1293       continue; /* already processed, skip */
1294     if ( (addr->sa_family == lal->addr.ss_family) &&
1295          (0 == memcmp (&ss,
1296                        &lal->addr,
1297                        addrlen)) )
1298     {
1299       /* Address unchanged, remember so we do not remove */
1300       lal->old = GNUNET_NO;
1301       return; /* done here */
1302     }
1303   }
1304   /* notify client, and remember IP for later removal! */
1305   lal = GNUNET_new (struct LocalAddressList);
1306   lal->addr = ss;
1307   lal->af = ss.ss_family;
1308   lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1309   GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1310                                ch->ext_addr_tail,
1311                                lal);
1312   check_notify_client (lal,
1313                        ch,
1314                        GNUNET_YES);
1315 }
1316
1317
1318 /**
1319  * Resolve the `hole_external` name to figure out our 
1320  * external address from a manually punched hole.  The
1321  * port number has already been parsed, this task is 
1322  * responsible for periodically doing a DNS lookup.
1323  *
1324  * @param ch client handle to act upon
1325  */
1326 static void
1327 dyndns_lookup (void *cls)
1328 {
1329   struct ClientHandle *ch = cls;
1330   struct LocalAddressList *lal;
1331
1332   for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
1333     lal->old = GNUNET_YES;
1334   ch->ext_dns_task = NULL;
1335   ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external,
1336                                         AF_UNSPEC,
1337                                         GNUNET_TIME_UNIT_MINUTES,
1338                                         &process_external_ip,
1339                                         ch);  
1340 }
1341
1342
1343 /**
1344  * Resolve the `hole_external` name to figure out our 
1345  * external address from a manually punched hole.  The
1346  * given name may be "AUTO" in which case we should use
1347  * the IP address(es) we have from upnpc or other methods.
1348  * The name can also be an IP address, in which case we 
1349  * do not need to do DNS resolution.  Finally, we also
1350  * need to parse the port number.
1351  *
1352  * @param ch client handle to act upon
1353  */
1354 static void
1355 lookup_hole_external (struct ClientHandle *ch)
1356 {
1357   char *port;
1358   unsigned int pnum;
1359   struct sockaddr_in *s4;
1360   struct LocalAddressList *lal;
1361     
1362   port = strrchr (ch->hole_external, ':');
1363   if (NULL == port)
1364   {
1365     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1366                 _("Malformed punched hole specification `%s' (lacks port)\n"),
1367                 ch->hole_external);
1368     return;
1369   }
1370   if ( (1 != sscanf (port + 1,
1371                      "%u",
1372                      &pnum)) ||
1373        (pnum > 65535) )
1374   {
1375     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1376                 _("Invalid port number in punched hole specification `%s' (lacks port)\n"),
1377                 port + 1);
1378     return;    
1379   }
1380   ch->ext_dns_port = (uint16_t) pnum;
1381   *port = '\0';
1382
1383   lal = GNUNET_new (struct LocalAddressList);
1384   if ('[' == *ch->hole_external)
1385   {
1386     struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr;
1387
1388     s6->sin6_family = AF_INET6;
1389     if (']' != (ch->hole_external[strlen(ch->hole_external)-1]))
1390     {
1391       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1392                   _("Malformed punched hole specification `%s' (lacks `]')\n"),
1393                   ch->hole_external);
1394       GNUNET_free (lal);
1395       return;
1396     }
1397     ch->hole_external[strlen(ch->hole_external)-1] = '\0';
1398     if (1 != inet_pton (AF_INET6,
1399                         ch->hole_external + 1,
1400                         &s6->sin6_addr))
1401     {
1402       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1403                   _("Malformed punched hole specification `%s' (IPv6 address invalid)"),
1404                   ch->hole_external + 1);
1405       GNUNET_free (lal);
1406       return;
1407     }
1408     s6->sin6_port = htons (ch->ext_dns_port);
1409     lal->af = AF_INET6;
1410     lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1411     GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1412                                  ch->ext_addr_tail,
1413                                  lal);
1414     check_notify_client (lal,
1415                          ch,
1416                          GNUNET_YES);
1417     return;
1418   }
1419
1420   s4 = (struct sockaddr_in *) &lal->addr;
1421   s4->sin_family = AF_INET;
1422   if (1 == inet_pton (AF_INET,
1423                       ch->hole_external,
1424                       &s4->sin_addr))
1425   {
1426     s4->sin_port = htons (ch->ext_dns_port);
1427     lal->af = AF_INET;
1428     lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1429     GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1430                                  ch->ext_addr_tail,
1431                                  lal);
1432     check_notify_client (lal,
1433                          ch,
1434                          GNUNET_YES);
1435     return;
1436   }
1437   if (0 == strcasecmp (ch->hole_external,
1438                        "AUTO"))
1439   {
1440     // FIXME: use `external-ip` address(es)!
1441     GNUNET_break (0); // not implemented!
1442     GNUNET_free (lal);
1443     return;
1444   }
1445   /* got a DNS name, trigger lookup! */
1446   GNUNET_free (lal);
1447   ch->ext_dns_task
1448     = GNUNET_SCHEDULER_add_now (&dyndns_lookup,
1449                                 ch);  
1450 }
1451
1452
1453 /**
1454  * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
1455  * We remember the client for updates upon future NAT events.
1456  *
1457  * @param cls client who sent the message
1458  * @param message the message received
1459  */
1460 static void
1461 handle_register (void *cls,
1462                  const struct GNUNET_NAT_RegisterMessage *message)
1463 {
1464   struct ClientHandle *ch = cls;
1465   const char *off;
1466   size_t left;
1467
1468   if ( (0 != ch->proto) ||
1469        (NULL != ch->caddrs) )
1470   {
1471     /* double registration not allowed */
1472     GNUNET_break (0);
1473     GNUNET_SERVICE_client_drop (ch->client);
1474     return;
1475   }
1476   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1477               "Received REGISTER message from client\n");
1478   ch->flags = message->flags;
1479   ch->proto = message->proto;
1480   ch->num_caddrs = ntohs (message->num_addrs);
1481   ch->caddrs = GNUNET_new_array (ch->num_caddrs,
1482                                  struct ClientAddress);
1483   left = ntohs (message->header.size) - sizeof (*message);
1484   off = (const char *) &message[1];
1485   for (unsigned int i=0;i<ch->num_caddrs;i++)
1486   {
1487     size_t alen;
1488     const struct sockaddr *sa = (const struct sockaddr *) off;
1489     uint16_t port;
1490     int is_nat;
1491
1492     if (sizeof (sa_family_t) > left)
1493     {
1494       GNUNET_break (0);
1495       GNUNET_SERVICE_client_drop (ch->client);
1496       return;
1497     }
1498     is_nat = GNUNET_NO;
1499     switch (sa->sa_family)
1500     {
1501     case AF_INET:
1502       {
1503         const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa;
1504         
1505         alen = sizeof (struct sockaddr_in);
1506         if (is_nat_v4 (&s4->sin_addr))
1507           is_nat = GNUNET_YES;
1508         port = ntohs (s4->sin_port);
1509       }
1510       break;
1511     case AF_INET6:
1512       {
1513         const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa;
1514         
1515         alen = sizeof (struct sockaddr_in6);
1516         if (is_nat_v6 (&s6->sin6_addr))
1517           is_nat = GNUNET_YES;
1518         port = ntohs (s6->sin6_port);
1519       }
1520       break;
1521 #if AF_UNIX
1522     case AF_UNIX:
1523       alen = sizeof (struct sockaddr_un);
1524       port = 0;
1525       break;
1526 #endif
1527     default:
1528       GNUNET_break (0);
1529       GNUNET_SERVICE_client_drop (ch->client);
1530       return;      
1531     }
1532     /* store address */
1533     GNUNET_assert (alen <= left);
1534     GNUNET_assert (alen <= sizeof (struct sockaddr_storage));
1535     GNUNET_memcpy (&ch->caddrs[i].ss,
1536                    sa,
1537                    alen);    
1538
1539     /* If applicable, try UPNPC NAT punching */
1540     if ( (is_nat) &&
1541          (enable_upnp) &&
1542          ( (IPPROTO_TCP == ch->proto) ||
1543            (IPPROTO_UDP == ch->proto) ) )
1544     {
1545       ch->natted_address = GNUNET_YES;
1546       ch->caddrs[i].mh
1547         = GNUNET_NAT_mini_map_start (port,
1548                                      IPPROTO_TCP == ch->proto,
1549                                      &upnp_addr_change_cb,
1550                                      ch);
1551     }
1552
1553     off += alen;
1554   }
1555
1556   ch->hole_external
1557     = GNUNET_strndup (off,
1558                       ntohs (message->hole_external_len));
1559   if (0 != ntohs (message->hole_external_len))
1560     lookup_hole_external (ch);
1561   
1562   /* Actually send IP address list to client */
1563   for (struct LocalAddressList *lal = lal_head;
1564        NULL != lal;
1565        lal = lal->next)
1566   {
1567     check_notify_client (lal,
1568                          ch,
1569                          GNUNET_YES);
1570   }
1571   /* Also consider IPv4 determined by `external-ip` */
1572   ch->external_monitor
1573     = GN_external_ipv4_monitor_start (&notify_client_external_ipv4_change,
1574                                       ch);
1575   GNUNET_SERVICE_client_continue (ch->client);
1576 }
1577
1578
1579 /**
1580  * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1581  * client.
1582  *
1583  * @param cls client who sent the message
1584  * @param message the message received
1585  * @return #GNUNET_OK if message is well-formed
1586  */
1587 static int
1588 check_stun (void *cls,
1589             const struct GNUNET_NAT_HandleStunMessage *message)
1590 {
1591   size_t sa_len = ntohs (message->sender_addr_size);
1592   size_t expect = sa_len + ntohs (message->payload_size);
1593   
1594   if (ntohs (message->header.size) - sizeof (*message) != expect)
1595   {
1596     GNUNET_break (0);
1597     return GNUNET_SYSERR;
1598   }
1599   if (sa_len < sizeof (sa_family_t))
1600   {
1601     GNUNET_break (0);
1602     return GNUNET_SYSERR;
1603   }
1604   return GNUNET_OK;
1605 }
1606
1607
1608 /**
1609  * Notify all clients about our external IP address
1610  * as reported by the STUN server.
1611  *
1612  * @param ip the external IP
1613  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
1614  */
1615 static void
1616 notify_clients_stun_change (const struct sockaddr_in *ip,
1617                             int add)
1618 {
1619   for (struct ClientHandle *ch = ch_head;
1620        NULL != ch;
1621        ch = ch->next)
1622   {
1623     struct sockaddr_in v4;
1624     struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
1625     struct GNUNET_MQ_Envelope *env;
1626     
1627     if (! ch->natted_address)
1628       continue;
1629     v4 = *ip;
1630     v4.sin_port = htons (0);
1631     env = GNUNET_MQ_msg_extra (msg,
1632                                sizeof (v4),
1633                                GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
1634     msg->add_remove = htonl ((int32_t) add);
1635     msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN |
1636                              GNUNET_NAT_AC_GLOBAL);
1637     GNUNET_memcpy (&msg[1],
1638                    &v4,
1639                    sizeof (v4));
1640     GNUNET_MQ_send (ch->mq,
1641                     env);
1642   }
1643 }
1644
1645
1646 /**
1647  * Function to be called when we decide that an
1648  * external IP address as told to us by a STUN
1649  * server has gone stale.
1650  *
1651  * @param cls the `struct StunExternalIP` to drop
1652  */
1653 static void
1654 stun_ip_timeout (void *cls)
1655 {
1656   struct StunExternalIP *se = cls;
1657
1658   se->timeout_task = NULL;
1659   notify_clients_stun_change (&se->external_addr,
1660                               GNUNET_NO);
1661   GNUNET_CONTAINER_DLL_remove (se_head,
1662                                se_tail,
1663                                se);
1664   GNUNET_free (se);
1665 }
1666
1667
1668 /**
1669  * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1670  * client.
1671  *
1672  * @param cls client who sent the message
1673  * @param message the message received
1674  */
1675 static void
1676 handle_stun (void *cls,
1677              const struct GNUNET_NAT_HandleStunMessage *message)
1678 {
1679   struct ClientHandle *ch = cls;
1680   const char *buf = (const char *) &message[1];
1681   const struct sockaddr *sa;
1682   const void *payload;
1683   size_t sa_len;
1684   size_t payload_size;
1685   struct sockaddr_in external_addr;
1686
1687   sa_len = ntohs (message->sender_addr_size);
1688   payload_size = ntohs (message->payload_size);
1689   sa = (const struct sockaddr *) &buf[0];
1690   payload = (const struct sockaddr *) &buf[sa_len];
1691   switch (sa->sa_family)
1692   {
1693   case AF_INET:
1694     if (sa_len != sizeof (struct sockaddr_in))
1695     {
1696       GNUNET_break (0);
1697       GNUNET_SERVICE_client_drop (ch->client);
1698       return;
1699     }
1700     break;
1701   case AF_INET6:
1702     if (sa_len != sizeof (struct sockaddr_in6))
1703     {
1704       GNUNET_break (0);
1705       GNUNET_SERVICE_client_drop (ch->client);
1706       return;
1707     }
1708     break;
1709   }
1710   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1711               "Received HANDLE_STUN message from client\n");
1712   if (GNUNET_OK ==
1713       GNUNET_NAT_stun_handle_packet_ (payload,
1714                                       payload_size,
1715                                       &external_addr))
1716   {     
1717     /* We now know that a server at "sa" claims that
1718        we are visible at IP "external_addr". 
1719
1720        We should (for some fixed period of time) tell
1721        all of our clients that listen to a NAT'ed address
1722        that they might want to consider the given 'external_ip'
1723        as their public IP address (this includes TCP and UDP
1724        clients, even if only UDP sends STUN requests).
1725
1726        If we do not get a renewal, the "external_addr" should be
1727        removed again.  The timeout frequency should be configurable
1728        (with a sane default), so that the UDP plugin can tell how
1729        often to re-request STUN.
1730     */
1731     struct StunExternalIP *se;
1732
1733     /* Check if we had a prior response from this STUN server */
1734     for (se = se_head; NULL != se; se = se->next)
1735     {
1736       if ( (se->stun_server_addr_len != sa_len) ||
1737            (0 != memcmp (sa,
1738                          &se->stun_server_addr,
1739                          sa_len)) )
1740         continue; /* different STUN server */
1741       if (0 != memcmp (&external_addr,
1742                        &se->external_addr,
1743                        sizeof (struct sockaddr_in)))
1744       {
1745         /* external IP changed, update! */
1746         notify_clients_stun_change (&se->external_addr,
1747                                     GNUNET_NO);
1748         se->external_addr = external_addr;
1749         notify_clients_stun_change (&se->external_addr,
1750                                     GNUNET_YES);
1751       }
1752       /* update timeout */
1753       GNUNET_SCHEDULER_cancel (se->timeout_task);
1754       se->timeout_task
1755         = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1756                                         &stun_ip_timeout,
1757                                         se);
1758       return;
1759     }
1760     /* STUN server is completely new, create fresh entry */
1761     se = GNUNET_new (struct StunExternalIP);
1762     se->external_addr = external_addr;
1763     GNUNET_memcpy (&se->stun_server_addr,
1764                    sa,
1765                    sa_len);
1766     se->stun_server_addr_len = sa_len;
1767     se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1768                                                      &stun_ip_timeout,
1769                                                      se);
1770     GNUNET_CONTAINER_DLL_insert (se_head,
1771                                  se_tail,
1772                                  se);
1773     notify_clients_stun_change (&se->external_addr,
1774                                 GNUNET_NO);
1775   }
1776   GNUNET_SERVICE_client_continue (ch->client);
1777 }
1778
1779
1780 /**
1781  * Check validity of
1782  * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from
1783  * client.
1784  *
1785  * @param cls client who sent the message
1786  * @param message the message received
1787  * @return #GNUNET_OK if message is well-formed
1788  */
1789 static int
1790 check_request_connection_reversal (void *cls,
1791                                    const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1792 {
1793   size_t expect;
1794
1795   expect = ntohs (message->local_addr_size)
1796     + ntohs (message->remote_addr_size);
1797   if (ntohs (message->header.size) - sizeof (*message) != expect)
1798   {
1799     GNUNET_break (0);
1800     return GNUNET_SYSERR;
1801   }
1802   return GNUNET_OK;
1803 }
1804
1805
1806 /**
1807  * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL
1808  * message from client.
1809  *
1810  * @param cls client who sent the message
1811  * @param message the message received
1812  */
1813 static void
1814 handle_request_connection_reversal (void *cls,
1815                                     const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1816 {
1817   struct ClientHandle *ch = cls;
1818   const char *buf = (const char *) &message[1];
1819   size_t local_sa_len = ntohs (message->local_addr_size);
1820   size_t remote_sa_len = ntohs (message->remote_addr_size);
1821   const struct sockaddr *local_sa = (const struct sockaddr *) &buf[0];
1822   const struct sockaddr *remote_sa = (const struct sockaddr *) &buf[local_sa_len];
1823   const struct sockaddr_in *l4 = NULL;
1824   const struct sockaddr_in *r4;
1825   int ret;
1826   
1827   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1828               "Received REQUEST CONNECTION REVERSAL message from client\n");
1829   switch (local_sa->sa_family)
1830   {
1831   case AF_INET:
1832     if (local_sa_len != sizeof (struct sockaddr_in))
1833     {
1834       GNUNET_break (0);
1835       GNUNET_SERVICE_client_drop (ch->client);
1836       return;
1837     }
1838     l4 = (const struct sockaddr_in *) local_sa;    
1839     break;
1840   case AF_INET6:
1841     if (local_sa_len != sizeof (struct sockaddr_in6))
1842     {
1843       GNUNET_break (0);
1844       GNUNET_SERVICE_client_drop (ch->client);
1845       return;
1846     }
1847     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1848                 _("Connection reversal for IPv6 not supported yet\n"));
1849     ret = GNUNET_SYSERR;
1850     break;
1851   default:
1852     GNUNET_break (0);
1853     GNUNET_SERVICE_client_drop (ch->client);
1854     return;
1855   }
1856   switch (remote_sa->sa_family)
1857   {
1858   case AF_INET:
1859     if (remote_sa_len != sizeof (struct sockaddr_in))
1860     {
1861       GNUNET_break (0);
1862       GNUNET_SERVICE_client_drop (ch->client);
1863       return;
1864     }
1865     r4 = (const struct sockaddr_in *) remote_sa;
1866     ret = GN_request_connection_reversal (&l4->sin_addr,
1867                                           ntohs (l4->sin_port),
1868                                           &r4->sin_addr);
1869     break;
1870   case AF_INET6:
1871     if (remote_sa_len != sizeof (struct sockaddr_in6))
1872     {
1873       GNUNET_break (0);
1874       GNUNET_SERVICE_client_drop (ch->client);
1875       return;
1876     }
1877     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1878                 _("Connection reversal for IPv6 not supported yet\n"));
1879     ret = GNUNET_SYSERR;
1880     break;
1881   default:
1882     GNUNET_break (0);
1883     GNUNET_SERVICE_client_drop (ch->client);
1884     return;
1885   }
1886   if (GNUNET_OK != ret)
1887     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1888                 _("Connection reversal request failed\n"));  
1889   GNUNET_SERVICE_client_continue (ch->client);
1890 }
1891
1892
1893 /**
1894  * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message
1895  * from client.
1896  *
1897  * @param cls client who sent the message
1898  * @param message the message received
1899  * @return #GNUNET_OK if message is well-formed
1900  */
1901 static int
1902 check_autoconfig_request (void *cls,
1903                           const struct GNUNET_NAT_AutoconfigRequestMessage *message)
1904 {
1905   return GNUNET_OK;  /* checked later */
1906 }
1907
1908
1909 /**
1910  * Stop all pending activities with respect to the @a ac
1911  *
1912  * @param ac autoconfiguration to terminate activities for
1913  */
1914 static void
1915 terminate_ac_activities (struct AutoconfigContext *ac)
1916 {
1917   if (NULL != ac->probe_external)
1918   {
1919     GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external);
1920     ac->probe_external = NULL;
1921   }
1922   if (NULL != ac->timeout_task)
1923   {
1924     GNUNET_SCHEDULER_cancel (ac->timeout_task);
1925     ac->timeout_task = NULL;
1926   }
1927 }
1928
1929
1930 /**
1931  * Finish handling the autoconfiguration request and send
1932  * the response to the client.
1933  *
1934  * @param cls the `struct AutoconfigContext` to conclude
1935  */
1936 static void
1937 conclude_autoconfig_request (void *cls)
1938 {
1939   struct AutoconfigContext *ac = cls;
1940   struct ClientHandle *ch = ac->ch;
1941   struct GNUNET_NAT_AutoconfigResultMessage *arm;
1942   struct GNUNET_MQ_Envelope *env;
1943   size_t c_size;
1944   char *buf;
1945   struct GNUNET_CONFIGURATION_Handle *diff;
1946   
1947   ac->timeout_task = NULL;
1948   terminate_ac_activities (ac);
1949
1950   /* Send back response */
1951   diff = GNUNET_CONFIGURATION_get_diff (ac->orig,
1952                                         ac->c);
1953   buf = GNUNET_CONFIGURATION_serialize (diff,
1954                                         &c_size);
1955   GNUNET_CONFIGURATION_destroy (diff);
1956   env = GNUNET_MQ_msg_extra (arm,
1957                              c_size,
1958                              GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
1959   arm->status_code = htonl ((uint32_t) ac->status_code);
1960   arm->type = htonl ((uint32_t) ac->type);
1961   GNUNET_memcpy (&arm[1],
1962                  buf,
1963                  c_size);
1964   GNUNET_free (buf);
1965   GNUNET_MQ_send (ch->mq,
1966                   env);
1967
1968   /* clean up */
1969   GNUNET_free (ac->system_type);
1970   GNUNET_CONFIGURATION_destroy (ac->orig);
1971   GNUNET_CONFIGURATION_destroy (ac->c);
1972   GNUNET_CONTAINER_DLL_remove (ac_head,
1973                                ac_tail,
1974                                ac);
1975   GNUNET_free (ac);
1976   GNUNET_SERVICE_client_continue (ch->client);
1977 }
1978
1979
1980 /**
1981  * Check if all autoconfiguration operations have concluded,
1982  * and if they have, send the result back to the client.
1983  *
1984  * @param ac autoconfiguation context to check
1985  */
1986 static void
1987 check_autoconfig_finished (struct AutoconfigContext *ac)
1988 {
1989   if (NULL != ac->probe_external)
1990     return;
1991   GNUNET_SCHEDULER_cancel (ac->timeout_task);
1992   ac->timeout_task
1993     = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
1994                                 ac);
1995 }
1996
1997
1998 /**
1999  * Update ENABLE_UPNPC configuration option.
2000  *
2001  * @param ac autoconfiguration to update
2002  */
2003 static void
2004 update_enable_upnpc_option (struct AutoconfigContext *ac)
2005 {
2006   switch (ac->enable_upnpc)
2007   {
2008   case GNUNET_YES:
2009     GNUNET_CONFIGURATION_set_value_string (ac->c,
2010                                            "NAT",
2011                                            "ENABLE_UPNP",
2012                                            "YES");
2013     break;
2014   case GNUNET_NO:
2015     GNUNET_CONFIGURATION_set_value_string (ac->c,
2016                                            "NAT",
2017                                            "ENABLE_UPNP",
2018                                            "NO");
2019     break;
2020   case GNUNET_SYSERR:
2021     /* We are unsure, do not change option */
2022     break;
2023   }
2024 }
2025
2026
2027 /**
2028  * Handle result from external IP address probe during
2029  * autoconfiguration.
2030  *
2031  * @param cls our `struct AutoconfigContext`
2032  * @param addr the address, NULL on errors
2033  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
2034  */
2035 static void
2036 auto_external_result_cb (void *cls,
2037                          const struct in_addr *addr,
2038                          enum GNUNET_NAT_StatusCode result)
2039 {
2040   struct AutoconfigContext *ac = cls;
2041
2042   ac->probe_external = NULL;
2043   switch (result)
2044   {
2045   case GNUNET_NAT_ERROR_SUCCESS:
2046     ac->enable_upnpc = GNUNET_YES;
2047     break;
2048   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
2049   case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
2050   case GNUNET_NAT_ERROR_IPC_FAILURE:
2051     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2052                 "Disabling UPNPC: %d\n",
2053                 (int) result);
2054     ac->enable_upnpc = GNUNET_NO; /* did not work */
2055     break;
2056   default:
2057     GNUNET_break (0); /* unexpected */
2058     ac->enable_upnpc = GNUNET_SYSERR;
2059     break;    
2060   }
2061   update_enable_upnpc_option (ac);
2062   check_autoconfig_finished (ac);
2063 }
2064
2065
2066 /**
2067  * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
2068  * client.
2069  *
2070  * @param cls client who sent the message
2071  * @param message the message received
2072  */
2073 static void
2074 handle_autoconfig_request (void *cls,
2075                            const struct GNUNET_NAT_AutoconfigRequestMessage *message)
2076 {
2077   struct ClientHandle *ch = cls;
2078   size_t left = ntohs (message->header.size) - sizeof (*message);
2079   struct LocalAddressList *lal;
2080   struct AutoconfigContext *ac;
2081
2082   ac = GNUNET_new (struct AutoconfigContext);
2083   ac->status_code = GNUNET_NAT_ERROR_SUCCESS;
2084   ac->ch = ch;
2085   ac->c = GNUNET_CONFIGURATION_create ();
2086   if (GNUNET_OK !=
2087       GNUNET_CONFIGURATION_deserialize (ac->c,
2088                                         (const char *) &message[1],
2089                                         left,
2090                                         GNUNET_NO))
2091   {
2092     GNUNET_break (0);
2093     GNUNET_SERVICE_client_drop (ch->client);
2094     GNUNET_CONFIGURATION_destroy (ac->c);
2095     GNUNET_free (ac);
2096     return;
2097   }
2098   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2099               "Received REQUEST_AUTO_CONFIG message from client\n");
2100
2101   if (GNUNET_OK !=
2102       GNUNET_CONFIGURATION_get_value_string (ac->c,
2103                                              "PEER",
2104                                              "SYSTEM_TYPE",
2105                                              &ac->system_type))
2106     ac->system_type = GNUNET_strdup ("UNKNOWN");
2107
2108   GNUNET_CONTAINER_DLL_insert (ac_head,
2109                                ac_tail,
2110                                ac);
2111   ac->orig
2112     = GNUNET_CONFIGURATION_dup (ac->c);
2113   ac->timeout_task
2114     = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
2115                                     &conclude_autoconfig_request,
2116                                     ac);
2117   ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
2118   
2119   /* Probe for upnpc */
2120   if (GNUNET_SYSERR ==
2121       GNUNET_OS_check_helper_binary ("upnpc",
2122                                      GNUNET_NO,
2123                                      NULL))
2124   {
2125     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2126                 _("UPnP client `upnpc` command not found, disabling UPnP\n"));
2127     ac->enable_upnpc = GNUNET_NO;
2128   }
2129   else
2130   {
2131     for (lal = lal_head; NULL != lal; lal = lal->next)
2132       if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN))
2133         /* we are behind NAT, useful to try upnpc */
2134         ac->enable_upnpc = GNUNET_YES;
2135   }
2136   if (GNUNET_YES == ac->enable_upnpc)
2137   {
2138     /* If we are a mobile device, always leave it on as the network
2139        may change to one that supports UPnP anytime.  If we are
2140        stationary, check if our network actually supports UPnP, and if
2141        not, disable it. */
2142     if ( (0 == strcasecmp (ac->system_type,
2143                            "INFRASTRUCTURE")) ||
2144          (0 == strcasecmp (ac->system_type,
2145                            "DESKTOP")) )
2146     {
2147       /* Check if upnpc gives us an external IP */
2148       ac->probe_external
2149         = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb,
2150                                               ac);
2151     }
2152   }
2153   if (NULL == ac->probe_external)
2154     update_enable_upnpc_option (ac);
2155
2156   /* Finally, check if we are already done */  
2157   check_autoconfig_finished (ac);
2158 }
2159
2160
2161 /**
2162  * Task run during shutdown.
2163  *
2164  * @param cls unused
2165  */
2166 static void
2167 shutdown_task (void *cls)
2168 {
2169   struct StunExternalIP *se;
2170   struct AutoconfigContext *ac;
2171
2172   while (NULL != (ac = ac_head))
2173   {
2174     GNUNET_CONTAINER_DLL_remove (ac_head,
2175                                  ac_tail,
2176                                  ac);
2177     terminate_ac_activities (ac);
2178     GNUNET_free (ac);
2179   }
2180   while (NULL != (se = se_head))
2181   {
2182     GNUNET_CONTAINER_DLL_remove (se_head,
2183                                  se_tail,
2184                                  se);
2185     GNUNET_SCHEDULER_cancel (se->timeout_task);
2186     GNUNET_free (se);
2187   }
2188   GN_nat_status_changed (GNUNET_NO);
2189   if (NULL != scan_task)
2190   {
2191     GNUNET_SCHEDULER_cancel (scan_task);
2192     scan_task = NULL;
2193   }
2194   if (NULL != stats)
2195   {
2196     GNUNET_STATISTICS_destroy (stats,
2197                                GNUNET_NO);
2198     stats = NULL;
2199   }
2200   destroy_lal ();
2201 }
2202
2203
2204 /**
2205  * Setup NAT service.
2206  *
2207  * @param cls closure
2208  * @param c configuration to use
2209  * @param service the initialized service
2210  */
2211 static void
2212 run (void *cls,
2213      const struct GNUNET_CONFIGURATION_Handle *c,
2214      struct GNUNET_SERVICE_Handle *service)
2215 {
2216   cfg = c;
2217   if (GNUNET_OK !=
2218       GNUNET_CONFIGURATION_get_value_time (cfg,
2219                                            "NAT",
2220                                            "STUN_STALE",
2221                                            &stun_stale_timeout))
2222     stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
2223
2224   /* Check for UPnP */
2225   enable_upnp 
2226     = GNUNET_CONFIGURATION_get_value_yesno (cfg,
2227                                             "NAT",
2228                                             "ENABLE_UPNP");
2229   if (GNUNET_YES == enable_upnp)
2230   {
2231     /* check if it works */
2232     if (GNUNET_SYSERR ==
2233         GNUNET_OS_check_helper_binary ("upnpc",
2234                                        GNUNET_NO,
2235                                        NULL))
2236     {
2237       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2238                   _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
2239       enable_upnp = GNUNET_SYSERR;
2240     }
2241   }
2242   if (GNUNET_OK !=
2243       GNUNET_CONFIGURATION_get_value_time (cfg,
2244                                            "nat",
2245                                            "DYNDNS_FREQUENCY",
2246                                            &dyndns_frequency))
2247     dyndns_frequency = DYNDNS_FREQUENCY;
2248   
2249   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
2250                                  NULL);
2251   stats = GNUNET_STATISTICS_create ("nat",
2252                                     cfg);
2253   scan_task = GNUNET_SCHEDULER_add_now (&run_scan,
2254                                         NULL);
2255 }
2256
2257
2258 /**
2259  * Callback called when a client connects to the service.
2260  *
2261  * @param cls closure for the service
2262  * @param c the new client that connected to the service
2263  * @param mq the message queue used to send messages to the client
2264  * @return a `struct ClientHandle`
2265  */
2266 static void *
2267 client_connect_cb (void *cls,
2268                    struct GNUNET_SERVICE_Client *c,
2269                    struct GNUNET_MQ_Handle *mq)
2270 {
2271   struct ClientHandle *ch;
2272
2273   ch = GNUNET_new (struct ClientHandle);
2274   ch->mq = mq;
2275   ch->client = c;
2276   GNUNET_CONTAINER_DLL_insert (ch_head,
2277                                ch_tail,
2278                                ch);
2279   return ch;
2280 }
2281
2282
2283 /**
2284  * Callback called when a client disconnected from the service
2285  *
2286  * @param cls closure for the service
2287  * @param c the client that disconnected
2288  * @param internal_cls a `struct ClientHandle *`
2289  */
2290 static void
2291 client_disconnect_cb (void *cls,
2292                       struct GNUNET_SERVICE_Client *c,
2293                       void *internal_cls)
2294 {
2295   struct ClientHandle *ch = internal_cls;
2296   struct LocalAddressList *lal;
2297
2298   GNUNET_CONTAINER_DLL_remove (ch_head,
2299                                ch_tail,
2300                                ch);
2301   for (unsigned int i=0;i<ch->num_caddrs;i++)
2302   {
2303     if (NULL != ch->caddrs[i].mh)
2304     {
2305       GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh);
2306       ch->caddrs[i].mh = NULL;
2307     }
2308   }
2309   GNUNET_free_non_null (ch->caddrs);
2310   while (NULL != (lal = ch->ext_addr_head))
2311   {
2312     GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
2313                                  ch->ext_addr_tail,
2314                                  lal);
2315     GNUNET_free (lal);
2316   }
2317   if (NULL != ch->ext_dns_task)
2318   {
2319     GNUNET_SCHEDULER_cancel (ch->ext_dns_task);
2320     ch->ext_dns_task = NULL;
2321   }
2322   if (NULL != ch->external_monitor)
2323   {
2324     GN_external_ipv4_monitor_stop (ch->external_monitor);
2325     ch->external_monitor = NULL;
2326   }
2327   if (NULL != ch->ext_dns)
2328   {
2329     GNUNET_RESOLVER_request_cancel (ch->ext_dns);
2330     ch->ext_dns = NULL;
2331   }
2332   GNUNET_free (ch->hole_external);
2333   GNUNET_free (ch);
2334 }
2335
2336
2337 /**
2338  * Define "main" method using service macro.
2339  */
2340 GNUNET_SERVICE_MAIN
2341 ("nat",
2342  GNUNET_SERVICE_OPTION_NONE,
2343  &run,
2344  &client_connect_cb,
2345  &client_disconnect_cb,
2346  NULL,
2347  GNUNET_MQ_hd_var_size (register,
2348                         GNUNET_MESSAGE_TYPE_NAT_REGISTER,
2349                         struct GNUNET_NAT_RegisterMessage,
2350                         NULL),
2351  GNUNET_MQ_hd_var_size (stun,
2352                         GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN,
2353                         struct GNUNET_NAT_HandleStunMessage,
2354                         NULL),
2355  GNUNET_MQ_hd_var_size (request_connection_reversal,
2356                         GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL,
2357                         struct GNUNET_NAT_RequestConnectionReversalMessage,
2358                         NULL),
2359  GNUNET_MQ_hd_var_size (autoconfig_request,
2360                         GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG,
2361                         struct GNUNET_NAT_AutoconfigRequestMessage,
2362                         NULL),
2363  GNUNET_MQ_handler_end ());
2364
2365
2366 #if defined(LINUX) && defined(__GLIBC__)
2367 #include <malloc.h>
2368
2369 /**
2370  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
2371  */
2372 void __attribute__ ((constructor))
2373 GNUNET_ARM_memory_init ()
2374 {
2375   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
2376   mallopt (M_TOP_PAD, 1 * 1024);
2377   malloc_trim (0);
2378 }
2379 #endif
2380
2381 /* end of gnunet-service-nat.c */