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