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