Merge remote-tracking branch 'origin/master' into credentials
[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   return nh;
449 }
450
451
452 /**
453  * Check if an incoming message is a STUN message.
454  *
455  * @param data the packet
456  * @param len the length of the packet in @a data
457  * @return #GNUNET_YES if @a data is a STUN packet,
458  *         #GNUNET_NO if the packet is invalid (not a stun packet)
459  */
460 static int
461 test_stun_packet (const void *data,
462                   size_t len)
463 {
464   const struct stun_header *hdr;
465   const struct stun_attr *attr;
466   uint32_t advertised_message_size;
467   uint32_t message_magic_cookie;
468
469   /* On entry, 'len' is the length of the UDP payload. After the
470    * initial checks it becomes the size of unprocessed options,
471    * while 'data' is advanced accordingly.
472    */
473   if (len < sizeof(struct stun_header))
474   {
475     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476                 "STUN packet too short (only %d, wanting at least %d)\n",
477                 (int) len,
478                 (int) sizeof (struct stun_header));
479     return GNUNET_NO;
480   }
481   hdr = (const struct stun_header *) data;
482   /* Skip header as it is already in hdr */
483   len -= sizeof (struct stun_header);
484   data += sizeof (struct stun_header);
485
486   /* len as advertised in the message */
487   advertised_message_size = ntohs (hdr->msglen);
488
489   message_magic_cookie = ntohl (hdr->magic);
490   /* Compare if the cookie match */
491   if (STUN_MAGIC_COOKIE != message_magic_cookie)
492   {
493     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
494                 "Invalid magic cookie for STUN\n");
495     return GNUNET_NO;
496   }
497
498   if (advertised_message_size > len)
499   {
500     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501                 "Scrambled STUN packet length (got %d, expecting %d)\n",
502                 advertised_message_size,
503                 (int)len);
504     return GNUNET_NO;
505   }
506   len = advertised_message_size;
507   while (len > 0)
508   {
509     if (len < sizeof (struct stun_attr))
510     {
511       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512                   "Attribute too short in STUN packet (got %d, expecting %d)\n",
513                   (int) len,
514                   (int) sizeof(struct stun_attr));
515       return GNUNET_NO;
516     }
517     attr = (const struct stun_attr *) data;
518
519     /* compute total attribute length */
520     advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr);
521
522     /* Check if we still have space in our buffer */
523     if (advertised_message_size > len)
524     {
525       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526                   "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n",
527                   advertised_message_size,
528                   (int) len);
529       return GNUNET_NO;
530     }
531     data += advertised_message_size;
532     len -= advertised_message_size;
533   }
534   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535               "STUN Packet, msg %04x, length: %d\n",
536               ntohs (hdr->msgtype),
537               advertised_message_size);
538   return GNUNET_OK;
539 }
540
541
542 /**
543  * Handle an incoming STUN message.  This function is useful as
544  * some GNUnet service may be listening on a UDP port and might
545  * thus receive STUN messages while trying to receive other data.
546  * In this case, this function can be used to process replies
547  * to STUN requests.
548  *
549  * The function does some basic sanity checks on packet size and
550  * content, try to extract a bit of information.
551  * 
552  * At the moment this only processes BIND requests, and returns the
553  * externally visible address of the request to the rest of the
554  * NAT logic.
555  *
556  * @param nh handle to the NAT service
557  * @param sender_addr address from which we got @a data
558  * @param sender_addr_len number of bytes in @a sender_addr
559  * @param data the packet
560  * @param data_size number of bytes in @a data
561  * @return #GNUNET_OK on success
562  *         #GNUNET_NO if the packet is not a STUN packet
563  *         #GNUNET_SYSERR on internal error handling the packet
564  */
565 int
566 GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh,
567                                const struct sockaddr *sender_addr,
568                                size_t sender_addr_len,
569                                const void *data,
570                                size_t data_size)
571 {
572   struct GNUNET_MQ_Envelope *env;
573   struct GNUNET_NAT_HandleStunMessage *hsn;
574   char *buf;
575
576   if (GNUNET_YES !=
577       test_stun_packet (data,
578                         data_size))
579     return GNUNET_NO;
580   if (NULL == nh->mq)
581     return GNUNET_SYSERR;
582   env = GNUNET_MQ_msg_extra (hsn,
583                              data_size + sender_addr_len,
584                              GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN);
585   hsn->sender_addr_size = htons ((uint16_t) sender_addr_len);
586   hsn->payload_size = htons ((uint16_t) data_size);
587   buf = (char *) &hsn[1];
588   GNUNET_memcpy (buf,
589                  sender_addr,
590                  sender_addr_len);
591   buf += sender_addr_len;
592   GNUNET_memcpy (buf,
593                  data,
594                  data_size);
595   GNUNET_MQ_send (nh->mq,
596                   env);
597   return GNUNET_OK;
598 }
599
600
601 /**
602  * Test if the given address is (currently) a plausible IP address for
603  * this peer.  Mostly a convenience function so that clients do not
604  * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback
605  * has returned so far.
606  *
607  * @param nh the handle returned by register
608  * @param addr IP address to test (IPv4 or IPv6)
609  * @param addrlen number of bytes in @a addr
610  * @return #GNUNET_YES if the address is plausible,
611  *         #GNUNET_NO if the address is not plausible,
612  *         #GNUNET_SYSERR if the address is malformed
613  */
614 int
615 GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh,
616                          const void *addr,
617                          socklen_t addrlen)
618 {
619   struct AddrEntry *ae;
620
621   if ( (addrlen != sizeof (struct sockaddr_in)) &&
622        (addrlen != sizeof (struct sockaddr_in6)) )
623   {
624     GNUNET_break (0);
625     return GNUNET_SYSERR;
626   }
627   for (ae = nh->ae_head; NULL != ae; ae = ae->next)
628     if ( (addrlen == ae->addrlen) &&
629          (0 == memcmp (addr,
630                        &ae[1],
631                        addrlen)) )
632       return GNUNET_YES;
633   return GNUNET_NO;
634 }
635
636
637 /**
638  * We learned about a peer (possibly behind NAT) so run the
639  * gnunet-nat-client to send dummy ICMP responses to cause
640  * that peer to connect to us (connection reversal).
641  *
642  * @param nh handle (used for configuration)
643  * @param local_sa our local address of the peer (IPv4-only)
644  * @param remote_sa the remote address of the peer (IPv4-only)
645  * @return #GNUNET_SYSERR on error, 
646  *         #GNUNET_NO if connection reversal is unavailable,
647  *         #GNUNET_OK otherwise (presumably in progress)
648  */
649 int
650 GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh,
651                              const struct sockaddr_in *local_sa,
652                              const struct sockaddr_in *remote_sa)
653 {
654   struct GNUNET_MQ_Envelope *env;
655   struct GNUNET_NAT_RequestConnectionReversalMessage *req;
656   char *buf;
657
658   if (NULL == nh->mq)
659     return GNUNET_SYSERR;
660   env = GNUNET_MQ_msg_extra (req,
661                              2 * sizeof (struct sockaddr_in),
662                              GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL);
663   req->local_addr_size = htons (sizeof (struct sockaddr_in));
664   req->remote_addr_size = htons (sizeof (struct sockaddr_in));
665   buf = (char *) &req[1];
666   GNUNET_memcpy (buf,
667                  local_sa,
668                  sizeof (struct sockaddr_in));
669   buf += sizeof (struct sockaddr_in);
670   GNUNET_memcpy (buf,
671                  remote_sa,
672                  sizeof (struct sockaddr_in));
673   GNUNET_MQ_send (nh->mq,
674                   env);
675   return GNUNET_OK;
676 }
677
678
679 /**
680  * Stop port redirection and public IP address detection for the given
681  * handle.  This frees the handle, after having sent the needed
682  * commands to close open ports.
683  *
684  * @param nh the handle to stop
685  */
686 void
687 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh)
688 {
689   GNUNET_MQ_destroy (nh->mq);
690   GNUNET_free (nh->reg);
691   GNUNET_free (nh);
692 }
693
694
695
696 /**
697  * Handle to auto-configuration in progress.
698  */
699 struct GNUNET_NAT_AutoHandle
700 {
701
702   /**
703    * Configuration we use.
704    */
705   const struct GNUNET_CONFIGURATION_Handle *cfg;
706   
707   /**
708    * Message queue for communicating with the NAT service.
709    */
710   struct GNUNET_MQ_Handle *mq;
711
712   /**
713    * Function called with the result from the autoconfiguration.
714    */
715   GNUNET_NAT_AutoResultCallback arc;
716
717   /**
718    * Closure for @e arc.
719    */
720   void *arc_cls;
721
722 };
723
724
725 /**
726  * Converts `enum GNUNET_NAT_StatusCode` to string
727  *
728  * @param err error code to resolve to a string
729  * @return point to a static string containing the error code
730  */
731 const char *
732 GNUNET_NAT_status2string (enum GNUNET_NAT_StatusCode err)
733 {
734   switch (err)
735   {
736   case GNUNET_NAT_ERROR_SUCCESS:
737     return _ ("Operation Successful");
738   case GNUNET_NAT_ERROR_IPC_FAILURE:
739     return _ ("IPC failure");
740   case GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR:
741     return _ ("Failure in network subsystem, check permissions.");
742   case GNUNET_NAT_ERROR_TIMEOUT:
743     return _ ("Encountered timeout while performing operation");
744   case GNUNET_NAT_ERROR_NOT_ONLINE:
745     return _ ("detected that we are offline");
746   case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND:
747     return _ ("`upnpc` command not found");
748   case GNUNET_NAT_ERROR_UPNPC_FAILED:
749     return _ ("Failed to run `upnpc` command");
750   case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
751     return _ ("`upnpc' command took too long, process killed");
752   case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
753     return _ ("`upnpc' command failed to establish port mapping");
754   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
755     return _ ("`external-ip' command not found");
756   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
757     return _ ("Failed to run `external-ip` command");
758   case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
759     return _ ("`external-ip' command output invalid");
760   case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
761     return _ ("no valid address was returned by `external-ip'");
762   case GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO:
763     return _ ("Could not determine interface with internal/local network address");
764   case GNUNET_NAT_ERROR_HELPER_NAT_SERVER_NOT_FOUND:
765     return _ ("No functioning gnunet-helper-nat-server installation found");
766   case GNUNET_NAT_ERROR_NAT_TEST_START_FAILED:
767     return _ ("NAT test could not be initialized");
768   case GNUNET_NAT_ERROR_NAT_TEST_TIMEOUT:
769     return _ ("NAT test timeout reached");
770   case GNUNET_NAT_ERROR_NAT_REGISTER_FAILED:
771     return _ ("could not register NAT");
772   case GNUNET_NAT_ERROR_HELPER_NAT_CLIENT_NOT_FOUND:
773     return _ ("No working gnunet-helper-nat-client installation found");
774   default:
775     return "unknown status code";
776   }
777 }
778
779
780 /**
781  * Check result from autoconfiguration attempt.
782  *
783  * @param cls the `struct GNUNET_NAT_AutoHandle`
784  * @param res the result
785  * @return #GNUNET_OK if @a res is well-formed (always for now)
786  */
787 static int
788 check_auto_result (void *cls,
789                    const struct GNUNET_NAT_AutoconfigResultMessage *res)
790 {
791   return GNUNET_OK;
792 }
793
794
795 /**
796  * Handle result from autoconfiguration attempt.
797  *
798  * @param cls the `struct GNUNET_NAT_AutoHandle`
799  * @param res the result
800  */
801 static void
802 handle_auto_result (void *cls,
803                     const struct GNUNET_NAT_AutoconfigResultMessage *res)
804 {
805   struct GNUNET_NAT_AutoHandle *ah = cls;
806   size_t left;
807   struct GNUNET_CONFIGURATION_Handle *cfg;
808   enum GNUNET_NAT_Type type
809     = (enum GNUNET_NAT_Type) ntohl (res->type);
810   enum GNUNET_NAT_StatusCode status
811     = (enum GNUNET_NAT_StatusCode) ntohl (res->status_code);
812
813   left = ntohs (res->header.size) - sizeof (*res);
814   cfg = GNUNET_CONFIGURATION_create ();
815   if (GNUNET_OK !=
816       GNUNET_CONFIGURATION_deserialize (cfg,
817                                         (const char *) &res[1],
818                                         left,
819                                         GNUNET_NO))
820   {
821     GNUNET_break (0);
822     ah->arc (ah->arc_cls,
823              NULL,
824              GNUNET_NAT_ERROR_IPC_FAILURE,
825              type);
826   }
827   else
828   {
829     ah->arc (ah->arc_cls,
830              cfg,
831              status,
832              type);
833   }
834   GNUNET_CONFIGURATION_destroy (cfg);
835   GNUNET_NAT_autoconfig_cancel (ah);
836 }
837
838
839 /**
840  * Handle queue errors by reporting autoconfiguration failure.
841  *
842  * @param cls the `struct GNUNET_NAT_AutoHandle *`
843  * @param error details about the error
844  */
845 static void
846 ah_error_handler (void *cls,
847                   enum GNUNET_MQ_Error error)
848 {
849   struct GNUNET_NAT_AutoHandle *ah = cls;
850
851   ah->arc (ah->arc_cls,
852            NULL,
853            GNUNET_NAT_ERROR_IPC_FAILURE,
854            GNUNET_NAT_TYPE_UNKNOWN);
855   GNUNET_NAT_autoconfig_cancel (ah);
856 }
857
858
859 /**
860  * Start auto-configuration routine.  The transport adapters should
861  * be stopped while this function is called.
862  *
863  * @param cfg initial configuration
864  * @param cb function to call with autoconfiguration result
865  * @param cb_cls closure for @a cb
866  * @return handle to cancel operation
867  */
868 struct GNUNET_NAT_AutoHandle *
869 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
870                              GNUNET_NAT_AutoResultCallback cb,
871                              void *cb_cls)
872 {
873   struct GNUNET_NAT_AutoHandle *ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
874   struct GNUNET_MQ_MessageHandler handlers[] = {
875     GNUNET_MQ_hd_var_size (auto_result,
876                            GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT,
877                            struct GNUNET_NAT_AutoconfigResultMessage,
878                            ah),
879     GNUNET_MQ_handler_end ()
880   };
881   struct GNUNET_MQ_Envelope *env;
882   struct GNUNET_NAT_AutoconfigRequestMessage *req;
883   char *buf;
884   size_t size;
885
886   buf = GNUNET_CONFIGURATION_serialize (cfg,
887                                         &size);
888   if (size > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (*req))
889   {
890     GNUNET_break (0);
891     GNUNET_free (buf);
892     GNUNET_free (ah);
893     return NULL;
894   }
895   ah->arc = cb;
896   ah->arc_cls = cb_cls;
897   ah->mq = GNUNET_CLIENT_connecT (cfg,
898                                   "nat",
899                                   handlers,
900                                   &ah_error_handler,
901                                   ah);
902   if (NULL == ah->mq)
903   {
904     GNUNET_break (0);
905     GNUNET_free (buf);
906     GNUNET_free (ah);
907     return NULL;
908   }
909   env = GNUNET_MQ_msg_extra (req,
910                              size,
911                              GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG);
912   GNUNET_memcpy (&req[1],
913                  buf,
914                  size);
915   GNUNET_free (buf);
916   GNUNET_MQ_send (ah->mq,
917                   env);
918   return ah;
919 }
920
921
922 /**
923  * Abort autoconfiguration.
924  *
925  * @param ah handle for operation to abort
926  */
927 void
928 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
929 {
930   GNUNET_MQ_destroy (ah->mq);
931   GNUNET_free (ah);
932 }
933
934 /* end of nat_api.c */