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