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