481bc6fded54b95ada0476ca93b7542926f97b00
[oweals/gnunet.git] / src / nat / nat_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2016 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  * @author Christian Grothoff
23  * @author Milan Bouchet-Valat
24  *
25  * @file nat/nat_api.c
26  * Service for handling UPnP and NAT-PMP port forwarding
27  * and external IP address retrieval
28  */
29 #include "platform.h"
30 #include "gnunet_nat_service.h"
31 #include "nat.h"
32 #include "nat_stun.h"
33
34
35 /**
36  * Entry in DLL of addresses of this peer.
37  */
38 struct AddrEntry
39 {
40
41   /**
42    * DLL.
43    */
44   struct AddrEntry *next;
45
46   /**
47    * DLL.
48    */
49   struct AddrEntry *prev;
50
51   /**
52    * Address class of the address.
53    */
54   enum GNUNET_NAT_AddressClass ac;
55   
56   /**
57    * Number of bytes that follow.
58    */
59   socklen_t addrlen;
60 };
61
62
63 /**
64  * Handle for active NAT registrations.
65  */
66 struct GNUNET_NAT_Handle
67 {
68
69   /**
70    * Configuration we use.
71    */
72   const struct GNUNET_CONFIGURATION_Handle *cfg;
73   
74   /**
75    * Message queue for communicating with the NAT service.
76    */
77   struct GNUNET_MQ_Handle *mq;
78
79   /**
80    * Our registration message.
81    */
82   struct GNUNET_MessageHeader *reg;
83   
84   /**
85    * Head of address DLL.
86    */
87   struct AddrEntry *ae_head;
88
89   /**
90    * Tail of address DLL.
91    */
92   struct AddrEntry *ae_tail;
93
94   /**
95    * Function to call when our addresses change.
96    */
97   GNUNET_NAT_AddressCallback address_callback;
98   
99   /**
100    * Function to call when another peer requests connection reversal.
101    */
102   GNUNET_NAT_ReversalCallback reversal_callback;
103   
104   /**
105    * Closure for the various callbacks.
106    */
107   void *callback_cls;
108
109   /**
110    * Task scheduled to reconnect to the service.
111    */
112   struct GNUNET_SCHEDULER_Task *reconnect_task;
113
114   /**
115    * How long to wait until we reconnect.
116    */
117   struct GNUNET_TIME_Relative reconnect_delay;
118 };
119
120
121 /**
122  * Task to connect to the NAT service.
123  *
124  * @param cls our `struct GNUNET_NAT_Handle *`
125  */
126 static void
127 do_connect (void *cls);
128
129
130 /**
131  * Task to connect to the NAT service.
132  *
133  * @param nh handle to reconnect
134  */
135 static void
136 reconnect (struct GNUNET_NAT_Handle *nh)
137 {
138   struct AddrEntry *ae;
139   
140   if (NULL != nh->mq)
141   {
142     GNUNET_MQ_destroy (nh->mq);
143     nh->mq = NULL;
144   }
145   while (NULL != (ae = nh->ae_head))
146   {
147     GNUNET_CONTAINER_DLL_remove (nh->ae_head,
148                                  nh->ae_tail,
149                                  ae);
150     nh->address_callback (nh->callback_cls,
151                           GNUNET_NO,
152                           ae->ac,
153                           (const struct sockaddr *) &ae[1],
154                           ae->addrlen);
155     GNUNET_free (ae);
156   }
157   nh->reconnect_delay
158     = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay);
159   nh->reconnect_task
160     = GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay,
161                                     &do_connect,
162                                     nh);
163 }
164
165
166 /**
167  * Check connection reversal request.
168  *
169  * @param cls our `struct GNUNET_NAT_Handle`
170  * @param crm the message
171  * @return #GNUNET_OK if @a crm is well-formed
172  */
173 static int
174 check_connection_reversal_request (void *cls,
175                                    const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
176 {
177   if (ntohs (crm->header.size) !=
178       sizeof (*crm) +
179       ntohs (crm->local_addr_size) +
180       ntohs (crm->remote_addr_size) )
181   {
182     GNUNET_break (0);
183     return GNUNET_SYSERR;
184   }
185   if ( (sizeof (struct sockaddr_in) != ntohs (crm->local_addr_size)) ||
186        (sizeof (struct sockaddr_in) != ntohs (crm->remote_addr_size)) )
187   {
188     GNUNET_break (0);
189     return GNUNET_SYSERR;
190   }
191   return GNUNET_OK;
192 }
193
194   
195 /**
196  * Handle connection reversal request.
197  *
198  * @param cls our `struct GNUNET_NAT_Handle`
199  * @param crm the message
200  */
201 static void
202 handle_connection_reversal_request (void *cls,
203                                     const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm)
204 {
205   struct GNUNET_NAT_Handle *nh = cls;
206   const struct sockaddr_in *local_sa = (const struct sockaddr_in *) &crm[1];
207   const struct sockaddr_in *remote_sa = &local_sa[1];
208
209   nh->reversal_callback (nh->callback_cls,
210                          (const struct sockaddr *) local_sa,
211                          sizeof (struct sockaddr_in),
212                          (const struct sockaddr *) remote_sa,
213                          sizeof (struct sockaddr_in));
214 }
215
216
217 /**
218  * Check address change notification.
219  *
220  * @param cls our `struct GNUNET_NAT_Handle`
221  * @param acn the message
222  * @return #GNUNET_OK if @a crm is well-formed
223  */
224 static int
225 check_address_change_notification (void *cls,
226                                    const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
227 {
228   size_t alen = ntohs (acn->header.size) - sizeof (*acn);
229
230   switch (alen)
231   {
232   case sizeof (struct sockaddr_in):
233     {
234       const struct sockaddr_in *s4
235         = (const struct sockaddr_in *) &acn[1];
236       if (AF_INET != s4->sin_family)
237       {
238         GNUNET_break (0);
239         return GNUNET_SYSERR;
240       }
241     }
242     break;
243   case sizeof (struct sockaddr_in6):
244     {
245       const struct sockaddr_in6 *s6
246         = (const struct sockaddr_in6 *) &acn[1];
247       if (AF_INET6 != s6->sin6_family)
248       {
249         GNUNET_break (0);
250         return GNUNET_SYSERR;
251       }
252     }
253     break;
254   default:
255     GNUNET_break (0);
256     return GNUNET_SYSERR;
257   }
258   return GNUNET_OK;
259 }
260
261   
262 /**
263  * Handle connection reversal request.
264  *
265  * @param cls our `struct GNUNET_NAT_Handle`
266  * @param acn the message
267  */
268 static void
269 handle_address_change_notification (void *cls,
270                                     const struct GNUNET_NAT_AddressChangeNotificationMessage *acn)
271 {
272   struct GNUNET_NAT_Handle *nh = cls;
273   size_t alen = ntohs (acn->header.size) - sizeof (*acn);
274   const struct sockaddr *sa = (const struct sockaddr *) &acn[1];
275   enum GNUNET_NAT_AddressClass ac;
276   struct AddrEntry *ae;
277
278   ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class);
279   if (GNUNET_YES == ntohl (acn->add_remove))
280   {
281     ae = GNUNET_malloc (sizeof (*ae) + alen);
282     ae->ac = ac;
283     ae->addrlen = alen;
284     GNUNET_memcpy (&ae[1],
285                    sa,
286                    alen);
287     GNUNET_CONTAINER_DLL_insert (nh->ae_head,
288                                  nh->ae_tail,
289                                  ae);
290   }
291   else
292   {
293     for (ae = nh->ae_head; NULL != ae; ae = ae->next)
294       if ( (ae->addrlen == alen) &&
295            (0 == memcmp (&ae[1],
296                          sa,
297                          alen)) )
298         break;
299     if (NULL == ae)
300     {
301       GNUNET_break (0);
302       reconnect (nh);
303       return;
304     }
305     GNUNET_CONTAINER_DLL_remove (nh->ae_head,
306                                  nh->ae_tail,
307                                  ae);
308     GNUNET_free (ae);
309   }
310   nh->address_callback (nh->callback_cls,
311                         ntohl (acn->add_remove),
312                         ac,
313                         sa,
314                         alen);
315 }
316
317
318 /**
319  * Handle queue errors by reconnecting to NAT.
320  *
321  * @param cls the `struct GNUNET_NAT_Handle *`
322  * @param error details about the error
323  */
324 static void
325 mq_error_handler (void *cls,
326                   enum GNUNET_MQ_Error error)
327 {
328   struct GNUNET_NAT_Handle *nh = cls;
329
330   reconnect (nh);
331 }
332
333
334 /**
335  * Task to connect to the NAT service.
336  *
337  * @param cls our `struct GNUNET_NAT_Handle *`
338  */
339 static void
340 do_connect (void *cls)
341 {
342   struct GNUNET_NAT_Handle *nh = cls;
343   struct GNUNET_MQ_MessageHandler handlers[] = {
344     GNUNET_MQ_hd_var_size (connection_reversal_request,
345                            GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED,
346                            struct GNUNET_NAT_ConnectionReversalRequestedMessage,
347                            nh),
348     GNUNET_MQ_hd_var_size (address_change_notification,
349                            GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE,
350                            struct GNUNET_NAT_AddressChangeNotificationMessage,
351                            nh),
352     GNUNET_MQ_handler_end ()
353   };
354   struct GNUNET_MQ_Envelope *env;
355
356   nh->reconnect_task = NULL;
357   nh->mq = GNUNET_CLIENT_connecT (nh->cfg,
358                                   "nat",
359                                   handlers,
360                                   &mq_error_handler,
361                                   nh);
362   if (NULL == nh->mq)
363   {
364     reconnect (nh);
365     return;
366   }
367   env = GNUNET_MQ_msg_copy (nh->reg);
368   GNUNET_MQ_send (nh->mq,
369                   env);
370 }
371
372
373 /**
374  * Attempt to enable port redirection and detect public IP address
375  * contacting UPnP or NAT-PMP routers on the local network. Use @a
376  * addr to specify to which of the local host's addresses should the
377  * external port be mapped. The port is taken from the corresponding
378  * sockaddr_in[6] field.  The NAT module should call the given @a
379  * address_callback for any 'plausible' external address.
380  *
381  * @param cfg configuration to use
382  * @param proto protocol this is about, IPPROTO_TCP or IPPROTO_UDP
383  * @param adv_port advertised port (port we are either bound to or that our OS
384  *                 locally performs redirection from to our bound port).
385  * @param num_addrs number of addresses in @a addrs
386  * @param addrs list of local addresses packets should be redirected to
387  * @param addrlens actual lengths of the addresses in @a addrs
388  * @param address_callback function to call everytime the public IP address changes
389  * @param reversal_callback function to call if someone wants connection reversal from us,
390  *        NULL if connection reversal is not supported
391  * @param callback_cls closure for callbacks
392  * @return NULL on error, otherwise handle that can be used to unregister
393  */
394 struct GNUNET_NAT_Handle *
395 GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg,
396                      uint8_t proto,
397                      uint16_t adv_port,
398                      unsigned int num_addrs,
399                      const struct sockaddr **addrs,
400                      const socklen_t *addrlens,
401                      GNUNET_NAT_AddressCallback address_callback,
402                      GNUNET_NAT_ReversalCallback reversal_callback,
403                      void *callback_cls)
404 {
405   struct GNUNET_NAT_Handle *nh;
406   struct GNUNET_NAT_RegisterMessage *rm;
407   size_t len;
408   char *off;
409   
410   len = 0;
411   for (unsigned int i=0;i<num_addrs;i++)
412     len += addrlens[i];
413   if ( (len > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*rm)) ||
414        (num_addrs > UINT16_MAX) )
415   {
416     GNUNET_break (0);
417     return NULL;
418   }
419   rm = GNUNET_malloc (sizeof (*rm) + len);
420   rm->header.size = htons (sizeof (*rm) + len);
421   rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER);
422   rm->flags = GNUNET_NAT_RF_NONE;
423   if (NULL != address_callback)
424     rm->flags |= GNUNET_NAT_RF_ADDRESSES;
425   if (NULL != reversal_callback)
426     rm->flags |= GNUNET_NAT_RF_REVERSAL;
427   rm->proto = proto;
428   rm->adv_port = htons (adv_port);
429   rm->num_addrs = htons ((uint16_t) num_addrs);
430   off = (char *) &rm[1];
431   for (unsigned int i=0;i<num_addrs;i++)
432   {
433     switch (addrs[i]->sa_family)
434     {
435     case AF_INET:
436       if (sizeof (struct sockaddr_in) != addrlens[i])
437       {
438         GNUNET_break (0);
439         return NULL;
440       }
441       break;
442     case AF_INET6:
443       if (sizeof (struct sockaddr_in6) != addrlens[i])
444       {
445         GNUNET_break (0);
446         return NULL;
447       }
448       break;
449 #if AF_UNIX
450     case AF_UNIX:
451       if (sizeof (struct sockaddr_un) != addrlens[i])
452       {
453         GNUNET_break (0);
454         return NULL;
455       }
456       break;
457 #endif
458     default:
459       GNUNET_break (0);
460       return NULL;
461     }
462     GNUNET_memcpy (off,
463                    addrs[i],
464                    addrlens[i]);
465     off += addrlens[i];
466   }
467
468   nh = GNUNET_new (struct GNUNET_NAT_Handle);
469   nh->reg = &rm->header;
470   nh->cfg = cfg;
471   nh->address_callback = address_callback;
472   nh->reversal_callback = reversal_callback;
473   nh->callback_cls = callback_cls;
474   do_connect (nh);
475   return nh;
476 }
477
478
479 /**
480  * Check if an incoming message is a STUN message.
481  *
482  * @param data the packet
483  * @param len the length of the packet in @a data
484  * @return #GNUNET_YES if @a data is a STUN packet,
485  *         #GNUNET_NO if the packet is invalid (not a stun packet)
486  */
487 static int
488 test_stun_packet (const void *data,
489                   size_t len)
490 {
491   const struct stun_header *hdr;
492   const struct stun_attr *attr;
493   uint32_t advertised_message_size;
494   uint32_t message_magic_cookie;
495
496   /* On entry, 'len' is the length of the UDP payload. After the
497    * initial checks it becomes the size of unprocessed options,
498    * while 'data' is advanced accordingly.
499    */
500   if (len < sizeof(struct stun_header))
501   {
502     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503                 "STUN packet too short (only %d, wanting at least %d)\n",
504                 (int) len,
505                 (int) sizeof (struct stun_header));
506     return GNUNET_NO;
507   }
508   hdr = (const struct stun_header *) data;
509   /* Skip header as it is already in hdr */
510   len -= sizeof (struct stun_header);
511   data += sizeof (struct stun_header);
512
513   /* len as advertised in the message */
514   advertised_message_size = ntohs (hdr->msglen);
515
516   message_magic_cookie = ntohl (hdr->magic);
517   /* Compare if the cookie match */
518   if (STUN_MAGIC_COOKIE != message_magic_cookie)
519   {
520     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
521                 "Invalid magic cookie for STUN\n");
522     return GNUNET_NO;
523   }
524
525   if (advertised_message_size > len)
526   {
527     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528                 "Scrambled STUN packet length (got %d, expecting %d)\n",
529                 advertised_message_size,
530                 (int)len);
531     return GNUNET_NO;
532   }
533   len = advertised_message_size;
534   while (len > 0)
535   {
536     if (len < sizeof (struct stun_attr))
537     {
538       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
539                   "Attribute too short in STUN packet (got %d, expecting %d)\n",
540                   (int) len,
541                   (int) sizeof(struct stun_attr));
542       return GNUNET_NO;
543     }
544     attr = (const struct stun_attr *) data;
545
546     /* compute total attribute length */
547     advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
548
549     /* Check if we still have space in our buffer */
550     if (advertised_message_size > len)
551     {
552       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
553                   "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n",
554                   advertised_message_size,
555                   (int) len);
556       return GNUNET_NO;
557     }
558     data += advertised_message_size;
559     len -= advertised_message_size;
560   }
561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562               "STUN Packet, msg %04x, length: %d\n",
563               ntohs (hdr->msgtype),
564               advertised_message_size);
565   return GNUNET_OK;
566 }
567
568
569 /**
570  * Handle an incoming STUN message.  This function is useful as
571  * some GNUnet service may be listening on a UDP port and might
572  * thus receive STUN messages while trying to receive other data.
573  * In this case, this function can be used to process replies
574  * to STUN requests.
575  *
576  * The function does some basic sanity checks on packet size and
577  * content, try to extract a bit of information.
578  * 
579  * At the moment this only processes BIND requests, and returns the
580  * externally visible address of the request to the rest of the
581  * NAT logic.
582  *
583  * @param nh handle to the NAT service
584  * @param sender_addr address from which we got @a data
585  * @param sender_addr_len number of bytes in @a sender_addr
586  * @param data the packet
587  * @param data_size number of bytes in @a data
588  * @return #GNUNET_OK on success
589  *         #GNUNET_NO if the packet is not a STUN packet
590  *         #GNUNET_SYSERR on internal error handling the packet
591  */
592 int
593 GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh,
594                                const struct sockaddr *sender_addr,
595                                size_t sender_addr_len,
596                                const void *data,
597                                size_t data_size)
598 {
599   struct GNUNET_MQ_Envelope *env;
600   struct GNUNET_NAT_HandleStunMessage *hsn;
601   char *buf;
602
603   if (GNUNET_YES !=
604       test_stun_packet (data,
605                         data_size))
606     return GNUNET_NO;
607   if (NULL == nh->mq)
608     return GNUNET_SYSERR;
609   env = GNUNET_MQ_msg_extra (hsn,
610                              data_size + sender_addr_len,
611                              GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN);
612   hsn->sender_addr_size = htons ((uint16_t) sender_addr_len);
613   hsn->payload_size = htons ((uint16_t) data_size);
614   buf = (char *) &hsn[1];
615   GNUNET_memcpy (buf,
616                  sender_addr,
617                  sender_addr_len);
618   buf += sender_addr_len;
619   GNUNET_memcpy (buf,
620                  data,
621                  data_size);
622   GNUNET_MQ_send (nh->mq,
623                   env);
624   return GNUNET_OK;
625 }
626
627
628 /**
629  * Test if the given address is (currently) a plausible IP address for
630  * this peer.  Mostly a convenience function so that clients do not
631  * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback
632  * has returned so far.
633  *
634  * @param nh the handle returned by register
635  * @param addr IP address to test (IPv4 or IPv6)
636  * @param addrlen number of bytes in @a addr
637  * @return #GNUNET_YES if the address is plausible,
638  *         #GNUNET_NO if the address is not plausible,
639  *         #GNUNET_SYSERR if the address is malformed
640  */
641 int
642 GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh,
643                          const void *addr,
644                          socklen_t addrlen)
645 {
646   struct AddrEntry *ae;
647
648   if ( (addrlen != sizeof (struct sockaddr_in)) &&
649        (addrlen != sizeof (struct sockaddr_in6)) )
650   {
651     GNUNET_break (0);
652     return GNUNET_SYSERR;
653   }
654   for (ae = nh->ae_head; NULL != ae; ae = ae->next)
655     if ( (addrlen == ae->addrlen) &&
656          (0 == memcmp (addr,
657                        &ae[1],
658                        addrlen)) )
659       return GNUNET_YES;
660   return GNUNET_NO;
661 }
662
663
664 /**
665  * We learned about a peer (possibly behind NAT) so run the
666  * gnunet-nat-client to send dummy ICMP responses to cause
667  * that peer to connect to us (connection reversal).
668  *
669  * @param nh handle (used for configuration)
670  * @param local_sa our local address of the peer (IPv4-only)
671  * @param remote_sa the remote address of the peer (IPv4-only)
672  * @return #GNUNET_SYSERR on error, 
673  *         #GNUNET_NO if connection reversal is unavailable,
674  *         #GNUNET_OK otherwise (presumably in progress)
675  */
676 int
677 GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh,
678                              const struct sockaddr_in *local_sa,
679                              const struct sockaddr_in *remote_sa)
680 {
681   struct GNUNET_MQ_Envelope *env;
682   struct GNUNET_NAT_RequestConnectionReversalMessage *req;
683   char *buf;
684
685   if (NULL == nh->mq)
686     return GNUNET_SYSERR;
687   env = GNUNET_MQ_msg_extra (req,
688                              2 * sizeof (struct sockaddr_in),
689                              GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL);
690   req->local_addr_size = htons (sizeof (struct sockaddr_in));
691   req->remote_addr_size = htons (sizeof (struct sockaddr_in));
692   buf = (char *) &req[1];
693   GNUNET_memcpy (buf,
694                  local_sa,
695                  sizeof (struct sockaddr_in));
696   buf += sizeof (struct sockaddr_in);
697   GNUNET_memcpy (buf,
698                  remote_sa,
699                  sizeof (struct sockaddr_in));
700   GNUNET_MQ_send (nh->mq,
701                   env);
702   return GNUNET_OK;
703 }
704
705
706 /**
707  * Stop port redirection and public IP address detection for the given
708  * handle.  This frees the handle, after having sent the needed
709  * commands to close open ports.
710  *
711  * @param nh the handle to stop
712  */
713 void
714 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh)
715 {
716   GNUNET_MQ_destroy (nh->mq);
717   GNUNET_free (nh->reg);
718   GNUNET_free (nh);
719 }
720
721
722
723 /**
724  * Handle to auto-configuration in progress.
725  */
726 struct GNUNET_NAT_AutoHandle
727 {
728
729   /**
730    * Configuration we use.
731    */
732   const struct GNUNET_CONFIGURATION_Handle *cfg;
733   
734   /**
735    * Message queue for communicating with the NAT service.
736    */
737   struct GNUNET_MQ_Handle *mq;
738
739   /**
740    * Function called with the result from the autoconfiguration.
741    */
742   GNUNET_NAT_AutoResultCallback arc;
743
744   /**
745    * Closure for @e arc.
746    */
747   void *arc_cls;
748
749 };
750
751
752 /**
753  * Converts `enum GNUNET_NAT_StatusCode` to string
754  *
755  * @param err error code to resolve to a string
756  * @return point to a static string containing the error code
757  */
758 const char *
759 GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err)
760 {
761   switch (err)
762   {
763   case GNUNET_NAT_ERROR_SUCCESS:
764     return _ ("Operation Successful");
765   case GNUNET_NAT_ERROR_IPC_FAILURE:
766     return _ ("IPC failure");
767   case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR:
768     return _ ("Failure in network subsystem, check permissions.");
769   case GNUNET_NAT_ERROR_TIMEOUT:
770     return _ ("Encountered timeout while performing operation");
771   case GNUNET_NAT_ERROR_NOT_ONLINE:
772     return _ ("detected that we are offline");
773   case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND:
774     return _ ("`upnpc` command not found");
775   case GNUNET_NAT_ERROR_UPNPC_FAILED:
776     return _ ("Failed to run `upnpc` command");
777   case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
778     return _ ("`upnpc' command took too long, process killed");
779   case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
780     return _ ("`upnpc' command failed to establish port mapping");
781   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
782     return _ ("`external-ip' command not found");
783   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
784     return _ ("Failed to run `external-ip` command");
785   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
786     return _ ("`external-ip' command output invalid");
787   case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
788     return _ ("no valid address was returned by `external-ip'");
789   case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO:
790     return _ ("Could not determine interface with internal/local network address");
791   case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND:
792     return _ ("No functioning gnunet-helper-nat-server installation found");
793   case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED:
794     return _ ("NAT test could not be initialized");
795   case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT:
796     return _ ("NAT test timeout reached");
797   case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED:
798     return _ ("could not register NAT");
799   case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND:
800     return _ ("No working gnunet-helper-nat-client installation found");
801   default:
802     return "unknown status code";
803   }
804 }
805
806
807 /**
808  * Check result from autoconfiguration attempt.
809  *
810  * @param cls the `struct GNUNET_NAT_AutoHandle`
811  * @param res the result
812  * @return #GNUNET_OK if @a res is well-formed (always for now)
813  */
814 static int
815 check_auto_result (void *cls,
816                    const struct GNUNET_NAT_AutoconfigResultMessage *res)
817 {
818   return GNUNET_OK;
819 }
820
821
822 /**
823  * Handle result from autoconfiguration attempt.
824  *
825  * @param cls the `struct GNUNET_NAT_AutoHandle`
826  * @param res the result
827  */
828 static void
829 handle_auto_result (void *cls,
830                     const struct GNUNET_NAT_AutoconfigResultMessage *res)
831 {
832   struct GNUNET_NAT_AutoHandle *ah = cls;
833   size_t left;
834   struct GNUNET_CONFIGURATION_Handle *cfg;
835   enum GNUNET_NAT_Type type
836     = (enum GNUNET_NAT_Type) ntohl (res->type);
837   enum GNUNET_NAT_StatusCode status
838     = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code);
839
840   left = ntohs (res->header.size) - sizeof (*res);
841   cfg = GNUNET_CONFIGURATION_create ();
842   if (GNUNET_OK !=
843       GNUNET_CONFIGURATION_deserialize (cfg,
844                                         (const char *) &res[1],
845                                         left,
846                                         GNUNET_NO))
847   {
848     GNUNET_break (0);
849     ah->arc (ah->arc_cls,
850              NULL,
851              GNUNET_NAT_ERROR_IPC_FAILURE,
852              type);
853   }
854   else
855   {
856     ah->arc (ah->arc_cls,
857              cfg,
858              status,
859              type);
860   }
861   GNUNET_CONFIGURATION_destroy (cfg);
862   GNUNET_NAT_autoconfig_cancel (ah);
863 }
864
865
866 /**
867  * Handle queue errors by reporting autoconfiguration failure.
868  *
869  * @param cls the `struct GNUNET_NAT_AutoHandle *`
870  * @param error details about the error
871  */
872 static void
873 ah_error_handler (void *cls,
874                   enum GNUNET_MQ_Error error)
875 {
876   struct GNUNET_NAT_AutoHandle *ah = cls;
877
878   ah->arc (ah->arc_cls,
879            NULL,
880            GNUNET_NAT_ERROR_IPC_FAILURE,
881            GNUNET_NAT_TYPE_UNKNOWN);
882   GNUNET_NAT_autoconfig_cancel (ah);
883 }
884
885
886 /**
887  * Start auto-configuration routine.  The transport adapters should
888  * be stopped while this function is called.
889  *
890  * @param cfg initial configuration
891  * @param cb function to call with autoconfiguration result
892  * @param cb_cls closure for @a cb
893  * @return handle to cancel operation
894  */
895 struct GNUNET_NAT_AutoHandle *
896 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
897                              GNUNET_NAT_AutoResultCallback cb,
898                              void *cb_cls)
899 {
900   struct GNUNET_NAT_AutoHandle *ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
901   struct GNUNET_MQ_MessageHandler handlers[] = {
902     GNUNET_MQ_hd_var_size (auto_result,
903                            GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT,
904                            struct GNUNET_NAT_AutoconfigResultMessage,
905                            ah),
906     GNUNET_MQ_handler_end ()
907   };
908   struct GNUNET_MQ_Envelope *env;
909   struct GNUNET_NAT_AutoconfigRequestMessage *req;
910   char *buf;
911   size_t size;
912
913   buf = GNUNET_CONFIGURATION_serialize (cfg,
914                                         &size);
915   if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*req))
916   {
917     GNUNET_break (0);
918     GNUNET_free (buf);
919     GNUNET_free (ah);
920     return NULL;
921   }
922   ah->arc = cb;
923   ah->arc_cls = cb_cls;
924   ah->mq = GNUNET_CLIENT_connecT (cfg,
925                                   "nat",
926                                   handlers,
927                                   &ah_error_handler,
928                                   ah);
929   if (NULL == ah->mq)
930   {
931     GNUNET_break (0);
932     GNUNET_free (buf);
933     GNUNET_free (ah);
934     return NULL;
935   }
936   env = GNUNET_MQ_msg_extra (req,
937                              size,
938                              GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG);
939   GNUNET_memcpy (&req[1],
940                  buf,
941                  size);
942   GNUNET_free (buf);
943   GNUNET_MQ_send (ah->mq,
944                   env);
945   return ah;
946 }
947
948
949 /**
950  * Abort autoconfiguration.
951  *
952  * @param ah handle for operation to abort
953  */
954 void
955 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
956 {
957   GNUNET_MQ_destroy (ah->mq);
958   GNUNET_free (ah);
959 }
960
961 /* end of nat_api.c */