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