-disconnects DO happen
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-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  * @file util/resolver_api.c
23  * @brief resolver for writing a tool
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_resolver_service.h"
30 #include "resolver.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
35
36 /**
37  * Maximum supported length for a hostname
38  */
39 #define MAX_HOSTNAME 1024
40
41
42 /**
43  * Possible hostnames for "loopback".
44  */
45 static const char *loopback[] = {
46   "localhost",
47   "ip6-localnet",
48   NULL
49 };
50
51
52 /**
53  * Configuration.
54  */
55 static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
56
57 /**
58  * Our connection to the resolver service, created on-demand, but then
59  * persists until error or shutdown.
60  */
61 static struct GNUNET_MQ_Handle *mq;
62
63 /**
64  * Head of DLL of requests.
65  */
66 static struct GNUNET_RESOLVER_RequestHandle *req_head;
67
68 /**
69  * Tail of DLL of requests.
70  */
71 static struct GNUNET_RESOLVER_RequestHandle *req_tail;
72
73 /**
74  * How long should we wait to reconnect?
75  */
76 static struct GNUNET_TIME_Relative backoff;
77
78 /**
79  * Task for reconnecting.
80  */
81 static struct GNUNET_SCHEDULER_Task *r_task;
82
83 /**
84  * Task ID of shutdown task; only present while we have a
85  * connection to the resolver service.
86  */
87 static struct GNUNET_SCHEDULER_Task *s_task;
88
89
90 /**
91  * Handle to a request given to the resolver.  Can be used to cancel
92  * the request prior to the timeout or successful execution.  Also
93  * used to track our internal state for the request.
94  */
95 struct GNUNET_RESOLVER_RequestHandle
96 {
97
98   /**
99    * Next entry in DLL of requests.
100    */
101   struct GNUNET_RESOLVER_RequestHandle *next;
102
103   /**
104    * Previous entry in DLL of requests.
105    */
106   struct GNUNET_RESOLVER_RequestHandle *prev;
107
108   /**
109    * Callback if this is an name resolution request,
110    * otherwise NULL.
111    */
112   GNUNET_RESOLVER_AddressCallback addr_callback;
113
114   /**
115    * Callback if this is a reverse lookup request,
116    * otherwise NULL.
117    */
118   GNUNET_RESOLVER_HostnameCallback name_callback;
119
120   /**
121    * Closure for the callbacks.
122    */
123   void *cls;
124
125   /**
126    * When should this request time out?
127    */
128   struct GNUNET_TIME_Absolute timeout;
129
130   /**
131    * Task handle for making reply callbacks in numeric lookups
132    * asynchronous, and for timeout handling.
133    */
134   struct GNUNET_SCHEDULER_Task *task;
135
136   /**
137    * Desired address family.
138    */
139   int af;
140
141   /**
142    * Has this request been transmitted to the service?
143    * #GNUNET_YES if transmitted
144    * #GNUNET_YES if not transmitted
145    * #GNUNET_SYSERR when request was canceled
146    */
147   int was_transmitted;
148
149   /**
150    * Did we add this request to the queue?
151    */
152   int was_queued;
153
154   /**
155    * Desired direction (IP to name or name to IP)
156    */
157   int direction;
158
159   /**
160    * #GNUNET_YES if a response was received
161    */
162   int received_response;
163
164   /**
165    * Length of the data that follows this struct.
166    */
167   size_t data_len;
168 };
169
170
171 /**
172  * Check that the resolver service runs on localhost
173  * (or equivalent).
174  *
175  * @return #GNUNET_OK if the resolver is properly configured,
176  *         #GNUNET_SYSERR otherwise.
177  */
178 static int
179 check_config ()
180 {
181   char *hostname;
182   unsigned int i;
183   struct sockaddr_in v4;
184   struct sockaddr_in6 v6;
185
186   memset (&v4, 0, sizeof (v4));
187   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
188   v4.sin_family = AF_INET;
189 #if HAVE_SOCKADDR_IN_SIN_LEN
190   v4.sin_len = sizeof (v4);
191 #endif
192   memset (&v6, 0, sizeof (v6));
193   v6.sin6_family = AF_INET6;
194 #if HAVE_SOCKADDR_IN_SIN_LEN
195   v6.sin6_len = sizeof (v6);
196 #endif
197   if (GNUNET_OK !=
198       GNUNET_CONFIGURATION_get_value_string (resolver_cfg,
199                                              "resolver",
200                                              "HOSTNAME",
201                                              &hostname))
202   {
203     LOG (GNUNET_ERROR_TYPE_INFO,
204          _("Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"),
205          "HOSTNAME",
206          "resolver");
207     return GNUNET_SYSERR;
208   }
209   if ((1 == inet_pton (AF_INET, hostname, &v4)) ||
210       (1 == inet_pton (AF_INET6, hostname, &v6)))
211   {
212     GNUNET_free (hostname);
213     return GNUNET_OK;
214   }
215   i = 0;
216   while (NULL != loopback[i])
217     if (0 == strcasecmp (loopback[i++], hostname))
218     {
219       GNUNET_free (hostname);
220       return GNUNET_OK;
221     }
222   LOG (GNUNET_ERROR_TYPE_INFO,
223        _("Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"),
224        "localhost",
225        "HOSTNAME",
226        "resolver");
227   GNUNET_free (hostname);
228   return GNUNET_SYSERR;
229 }
230
231
232 /**
233  * Create the connection to the resolver service.
234  *
235  * @param cfg configuration to use
236  */
237 void
238 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
239 {
240   GNUNET_assert (NULL != cfg);
241   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
242   resolver_cfg = cfg;
243 }
244
245
246 /**
247  * Destroy the connection to the resolver service.
248  */
249 void
250 GNUNET_RESOLVER_disconnect ()
251 {
252   GNUNET_assert (NULL == req_head);
253   GNUNET_assert (NULL == req_tail);
254   if (NULL != mq)
255   {
256     LOG (GNUNET_ERROR_TYPE_DEBUG,
257          "Disconnecting from DNS service\n");
258     GNUNET_MQ_destroy (mq);
259     mq = NULL;
260   }
261   if (NULL != r_task)
262   {
263     GNUNET_SCHEDULER_cancel (r_task);
264     r_task = NULL;
265   }
266   if (NULL != s_task)
267   {
268     GNUNET_SCHEDULER_cancel (s_task);
269     s_task = NULL;
270   }
271 }
272
273
274 /**
275  * Convert IP address to string without DNS resolution.
276  *
277  * @param af address family
278  * @param ip the address
279  * @param ip_len number of bytes in @a ip
280  * @return address as a string, NULL on error
281  */
282 static char *
283 no_resolve (int af,
284             const void *ip,
285             socklen_t ip_len)
286 {
287   char buf[INET6_ADDRSTRLEN];
288
289   switch (af)
290   {
291   case AF_INET:
292     if (ip_len != sizeof (struct in_addr))
293       return NULL;
294     if (NULL ==
295         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
296     {
297       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
298                     "inet_ntop");
299       return NULL;
300     }
301     break;
302   case AF_INET6:
303     if (ip_len != sizeof (struct in6_addr))
304       return NULL;
305     if (NULL ==
306         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
307     {
308       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
309                     "inet_ntop");
310       return NULL;
311     }
312     break;
313   default:
314     GNUNET_break (0);
315     return NULL;
316   }
317   return GNUNET_strdup (buf);
318 }
319
320
321 /**
322  * Adjust exponential back-off and reconnect to the service.
323  */
324 static void
325 reconnect (void);
326
327
328 /**
329  * Generic error handler, called with the appropriate error code and
330  * the same closure specified at the creation of the message queue.
331  * Not every message queue implementation supports an error handler.
332  *
333  * @param cls NULL
334  * @param error error code
335  */
336 static void
337 mq_error_handler (void *cls,
338                   enum GNUNET_MQ_Error error)
339 {
340   GNUNET_MQ_destroy (mq);
341   mq = NULL;
342   reconnect ();
343 }
344
345
346 /**
347  * Task executed on system shutdown.
348  */
349 static void
350 shutdown_task (void *cls)
351 {
352   s_task = NULL;
353   GNUNET_RESOLVER_disconnect ();
354   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
355 }
356
357
358 /**
359  * Process pending requests to the resolver.
360  */
361 static void
362 process_requests ()
363 {
364   struct GNUNET_RESOLVER_GetMessage *msg;
365   struct GNUNET_MQ_Envelope *env;
366   struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
367
368   if (NULL == mq)
369   {
370     reconnect ();
371     return;
372   }
373   if (NULL == rh)
374   {
375     /* nothing to do, release socket really soon if there is nothing
376      * else happening... */
377     s_task =
378       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
379                                     &shutdown_task,
380                                     NULL);
381     return;
382   }
383   if (GNUNET_NO != rh->was_transmitted)
384     return;                     /* waiting for reply */
385   env = GNUNET_MQ_msg_extra (msg,
386                              rh->data_len,
387                              GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
388   msg->direction = htonl (rh->direction);
389   msg->af = htonl (rh->af);
390   memcpy (&msg[1],
391           &rh[1],
392           rh->data_len);
393   LOG (GNUNET_ERROR_TYPE_DEBUG,
394        "Transmitting DNS resolution request to DNS service\n");
395   GNUNET_MQ_send (mq,
396                   env);
397   rh->was_transmitted = GNUNET_YES;
398 }
399
400
401 /**
402  * Check validity of response with a hostname for a DNS lookup.
403  *
404  * @param cls NULL
405  * @param msg message with the hostname
406  */
407 static int
408 check_response (void *cls,
409                 const struct GNUNET_MessageHeader *msg)
410 {
411   /* implemented in #handle_response() for now */
412   return GNUNET_OK;
413 }
414
415
416 /**
417  * Check validity of response with a hostname for a DNS lookup.
418  * NOTE: right now rather messy, might want to use different
419  * message types for different response formats in the future.
420  *
421  * @param cls NULL
422  * @param msg message with the response
423  */
424 static void
425 handle_response (void *cls,
426                  const struct GNUNET_MessageHeader *msg)
427 {
428   struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
429   uint16_t size;
430   char *nret;
431
432   size = ntohs (msg->size);
433   if (size == sizeof (struct GNUNET_MessageHeader))
434   {
435     LOG (GNUNET_ERROR_TYPE_DEBUG,
436          "Received empty response from DNS service\n");
437     /* message contains not data, just header; end of replies */
438     /* check if request was canceled */
439     if (GNUNET_SYSERR != rh->was_transmitted)
440     {
441       /* no reverse lookup was successful, return IP as string */
442       if (NULL != rh->name_callback)
443       {
444         if (GNUNET_NO == rh->received_response)
445         {
446           nret = no_resolve (rh->af,
447                              &rh[1],
448                              rh->data_len);
449           rh->name_callback (rh->cls, nret);
450           GNUNET_free (nret);
451         }
452         /* finally, make termination call */
453         rh->name_callback (rh->cls,
454                            NULL);
455       }
456       if (NULL != rh->addr_callback)
457         rh->addr_callback (rh->cls,
458                            NULL,
459                            0);
460     }
461     rh->was_transmitted = GNUNET_NO;
462     GNUNET_RESOLVER_request_cancel (rh);
463     process_requests ();
464     return;
465   }
466   /* return reverse lookup results to caller */
467   if (NULL != rh->name_callback)
468   {
469     const char *hostname;
470
471     hostname = (const char *) &msg[1];
472     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
473     {
474       GNUNET_break (0);
475       if (GNUNET_SYSERR != rh->was_transmitted)
476         rh->name_callback (rh->cls,
477                            NULL);
478       rh->was_transmitted = GNUNET_NO;
479       GNUNET_RESOLVER_request_cancel (rh);
480       GNUNET_MQ_destroy (mq);
481       mq = NULL;
482       reconnect ();
483       return;
484     }
485     LOG (GNUNET_ERROR_TYPE_DEBUG,
486          "Resolver returns `%s' for IP `%s'.\n",
487          hostname,
488          GNUNET_a2s ((const void *) &rh[1],
489                      rh->data_len));
490     if (rh->was_transmitted != GNUNET_SYSERR)
491       rh->name_callback (rh->cls,
492                          hostname);
493     rh->received_response = GNUNET_YES;
494   }
495   /* return lookup results to caller */
496   if (NULL != rh->addr_callback)
497   {
498     struct sockaddr_in v4;
499     struct sockaddr_in6 v6;
500     const struct sockaddr *sa;
501     socklen_t salen;
502     const void *ip;
503     size_t ip_len;
504
505     ip = &msg[1];
506     ip_len = size - sizeof (struct GNUNET_MessageHeader);
507     if (ip_len == sizeof (struct in_addr))
508     {
509       memset (&v4, 0, sizeof (v4));
510       v4.sin_family = AF_INET;
511       v4.sin_addr = *(struct in_addr*) ip;
512 #if HAVE_SOCKADDR_IN_SIN_LEN
513       v4.sin_len = sizeof (v4);
514 #endif
515       salen = sizeof (v4);
516       sa = (const struct sockaddr *) &v4;
517     }
518     else if (ip_len == sizeof (struct in6_addr))
519     {
520       memset (&v6, 0, sizeof (v6));
521       v6.sin6_family = AF_INET6;
522       v6.sin6_addr = *(struct in6_addr*) ip;
523 #if HAVE_SOCKADDR_IN_SIN_LEN
524       v6.sin6_len = sizeof (v6);
525 #endif
526       salen = sizeof (v6);
527       sa = (const struct sockaddr *) &v6;
528     }
529     else
530     {
531       GNUNET_break (0);
532       if (GNUNET_SYSERR != rh->was_transmitted)
533         rh->addr_callback (rh->cls,
534                            NULL,
535                            0);
536       rh->was_transmitted = GNUNET_NO;
537       GNUNET_RESOLVER_request_cancel (rh);
538       GNUNET_MQ_destroy (mq);
539       mq = NULL;
540       reconnect ();
541       return;
542     }
543     LOG (GNUNET_ERROR_TYPE_DEBUG,
544          "Received IP from DNS service\n");
545     if (GNUNET_SYSERR != rh->was_transmitted)
546       rh->addr_callback (rh->cls,
547                          sa,
548                          salen);
549   }
550 }
551
552
553 /**
554  * We've been asked to lookup the address for a hostname and were
555  * given a valid numeric string.  Perform the callbacks for the
556  * numeric addresses.
557  *
558  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
559  */
560 static void
561 numeric_resolution (void *cls)
562 {
563   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
564   struct sockaddr_in v4;
565   struct sockaddr_in6 v6;
566   const char *hostname;
567
568   rh->task = NULL;
569   memset (&v4, 0, sizeof (v4));
570   v4.sin_family = AF_INET;
571 #if HAVE_SOCKADDR_IN_SIN_LEN
572   v4.sin_len = sizeof (v4);
573 #endif
574   memset (&v6, 0, sizeof (v6));
575   v6.sin6_family = AF_INET6;
576 #if HAVE_SOCKADDR_IN_SIN_LEN
577   v6.sin6_len = sizeof (v6);
578 #endif
579   hostname = (const char *) &rh[1];
580   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
581       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
582   {
583     rh->addr_callback (rh->cls,
584                        (const struct sockaddr *) &v4,
585                        sizeof (v4));
586     if ((rh->af == AF_UNSPEC) &&
587         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
588     {
589       /* this can happen on some systems IF "hostname" is "localhost" */
590       rh->addr_callback (rh->cls,
591                          (const struct sockaddr *) &v6,
592                          sizeof (v6));
593     }
594     rh->addr_callback (rh->cls,
595                        NULL,
596                        0);
597     GNUNET_free (rh);
598     return;
599   }
600   if ( ( (rh->af == AF_UNSPEC) ||
601          (rh->af == AF_INET6) ) &&
602        (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr) ) )
603   {
604     rh->addr_callback (rh->cls,
605                        (const struct sockaddr *) &v6,
606                        sizeof (v6));
607     rh->addr_callback (rh->cls,
608                        NULL,
609                        0);
610     GNUNET_free (rh);
611     return;
612   }
613   /* why are we here? this task should not have been scheduled! */
614   GNUNET_assert (0);
615   GNUNET_free (rh);
616 }
617
618
619 /**
620  * We've been asked to lookup the address for a hostname and were
621  * given a variant of "loopback".  Perform the callbacks for the
622  * respective loopback numeric addresses.
623  *
624  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
625  */
626 static void
627 loopback_resolution (void *cls)
628 {
629   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
630   struct sockaddr_in v4;
631   struct sockaddr_in6 v6;
632
633   rh->task = NULL;
634   memset (&v4, 0, sizeof (v4));
635   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
636   v4.sin_family = AF_INET;
637 #if HAVE_SOCKADDR_IN_SIN_LEN
638   v4.sin_len = sizeof (v4);
639 #endif
640   memset (&v6, 0, sizeof (v6));
641   v6.sin6_family = AF_INET6;
642 #if HAVE_SOCKADDR_IN_SIN_LEN
643   v6.sin6_len = sizeof (v6);
644 #endif
645   v6.sin6_addr = in6addr_loopback;
646   switch (rh->af)
647   {
648   case AF_INET:
649     rh->addr_callback (rh->cls,
650                        (const struct sockaddr *) &v4,
651                        sizeof (v4));
652     break;
653   case AF_INET6:
654     rh->addr_callback (rh->cls,
655                        (const struct sockaddr *) &v6,
656                        sizeof (v6));
657     break;
658   case AF_UNSPEC:
659     rh->addr_callback (rh->cls,
660                        (const struct sockaddr *) &v6,
661                        sizeof (v6));
662     rh->addr_callback (rh->cls,
663                        (const struct sockaddr *) &v4,
664                        sizeof (v4));
665
666     break;
667   default:
668     GNUNET_break (0);
669     break;
670   }
671   rh->addr_callback (rh->cls,
672                      NULL,
673                      0);
674   GNUNET_free (rh);
675 }
676
677
678 /**
679  * Now try to reconnect to the resolver service.
680  *
681  * @param cls NULL
682  */
683 static void
684 reconnect_task (void *cls)
685 {
686   GNUNET_MQ_hd_var_size (response,
687                          GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
688                          struct GNUNET_MessageHeader);
689   struct GNUNET_MQ_MessageHandler handlers[] = {
690      make_response_handler (NULL),
691      GNUNET_MQ_handler_end ()
692   };
693
694   r_task = NULL;
695   if (NULL == req_head)
696     return;                     /* no work pending */
697   LOG (GNUNET_ERROR_TYPE_DEBUG,
698        "Trying to connect to DNS service\n");
699   mq = GNUNET_CLIENT_connecT (resolver_cfg,
700                               "resolver",
701                               handlers,
702                               &mq_error_handler,
703                               NULL);
704   if (NULL == mq)
705   {
706     LOG (GNUNET_ERROR_TYPE_DEBUG,
707          "Failed to connect, will try again later\n");
708     reconnect ();
709     return;
710   }
711   process_requests ();
712 }
713
714
715 /**
716  * Adjust exponential back-off and reconnect to the service.
717  */
718 static void
719 reconnect ()
720 {
721   struct GNUNET_RESOLVER_RequestHandle *rh;
722
723   if (NULL != r_task)
724     return;
725   GNUNET_assert (NULL == mq);
726   if (NULL != (rh = req_head))
727   {
728     switch (rh->was_transmitted)
729     {
730     case GNUNET_NO:
731       /* nothing more to do */
732       break;
733     case GNUNET_YES:
734       /* disconnected, transmit again! */
735       rh->was_transmitted = GNUNET_NO;
736       break;
737     case GNUNET_SYSERR:
738       /* request was cancelled, remove entirely */
739       GNUNET_CONTAINER_DLL_remove (req_head,
740                                    req_tail,
741                                    rh);
742       GNUNET_free (rh);
743       break;
744     default:
745       GNUNET_assert (0);
746       break;
747     }
748   }
749   LOG (GNUNET_ERROR_TYPE_DEBUG,
750        "Will try to connect to DNS service in %s\n",
751        GNUNET_STRINGS_relative_time_to_string (backoff,
752                                                GNUNET_YES));
753   GNUNET_assert (NULL != resolver_cfg);
754   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
755                                          &reconnect_task,
756                                          NULL);
757   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
758 }
759
760
761 /**
762  * A DNS resolution timed out. Notify the application.
763  *
764  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
765  */
766 static void
767 handle_lookup_timeout (void *cls)
768 {
769   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
770
771   rh->task = NULL;
772   if (GNUNET_NO == rh->direction)
773   {
774     LOG (GNUNET_ERROR_TYPE_INFO,
775          _("Timeout trying to resolve hostname `%s'.\n"),
776          (const char *) &rh[1]);
777     if (NULL != rh->addr_callback)
778       rh->addr_callback (rh->cls,
779                          NULL,
780                          0);
781   }
782   else
783   {
784     char buf[INET6_ADDRSTRLEN];
785
786     LOG (GNUNET_ERROR_TYPE_INFO,
787          _("Timeout trying to resolve IP address `%s'.\n"),
788          inet_ntop (rh->af,
789                     (const void *) &rh[1],
790                     buf,
791                     sizeof(buf)));
792     if (GNUNET_NO == rh->received_response)
793     {
794       char *nret;
795
796       nret = no_resolve (rh->af,
797                          &rh[1],
798                          rh->data_len);
799       if (NULL != rh->name_callback)
800         rh->name_callback (rh->cls, nret);
801       GNUNET_free (nret);
802     }
803     /* finally, make termination call */
804     if (NULL != rh->name_callback)
805       rh->name_callback (rh->cls,
806                          NULL);
807   }
808   rh->was_transmitted = GNUNET_NO;
809   GNUNET_RESOLVER_request_cancel (rh);
810   process_requests ();
811 }
812
813
814 /**
815  * Convert a string to one or more IP addresses.
816  *
817  * @param hostname the hostname to resolve
818  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
819  * @param callback function to call with addresses
820  * @param callback_cls closure for @a callback
821  * @param timeout how long to try resolving
822  * @return handle that can be used to cancel the request, NULL on error
823  */
824 struct GNUNET_RESOLVER_RequestHandle *
825 GNUNET_RESOLVER_ip_get (const char *hostname,
826                         int af,
827                         struct GNUNET_TIME_Relative timeout,
828                         GNUNET_RESOLVER_AddressCallback callback,
829                         void *callback_cls)
830 {
831   struct GNUNET_RESOLVER_RequestHandle *rh;
832   size_t slen;
833   unsigned int i;
834   struct in_addr v4;
835   struct in6_addr v6;
836
837   slen = strlen (hostname) + 1;
838   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
839       GNUNET_SERVER_MAX_MESSAGE_SIZE)
840   {
841     GNUNET_break (0);
842     return NULL;
843   }
844   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
845   rh->af = af;
846   rh->addr_callback = callback;
847   rh->cls = callback_cls;
848   memcpy (&rh[1],
849           hostname,
850           slen);
851   rh->data_len = slen;
852   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
853   rh->direction = GNUNET_NO;
854   /* first, check if this is a numeric address */
855   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
856        ((af == AF_INET) || (af == AF_UNSPEC))) ||
857       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
858        ((af == AF_INET6) || (af == AF_UNSPEC))))
859   {
860     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
861                                          rh);
862     return rh;
863   }
864   /* then, check if this is a loopback address */
865   i = 0;
866   while (NULL != loopback[i])
867     if (0 == strcasecmp (loopback[i++],
868                          hostname))
869     {
870       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
871                                            rh);
872       return rh;
873     }
874   if (GNUNET_OK != check_config ())
875   {
876     GNUNET_free (rh);
877     return NULL;
878   }
879   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
880                                            &handle_lookup_timeout,
881                                            rh);
882   GNUNET_CONTAINER_DLL_insert_tail (req_head,
883                                     req_tail,
884                                     rh);
885   rh->was_queued = GNUNET_YES;
886   if (NULL != s_task)
887   {
888     GNUNET_SCHEDULER_cancel (s_task);
889     s_task = NULL;
890   }
891   process_requests ();
892   return rh;
893 }
894
895
896 /**
897  * We've been asked to convert an address to a string without
898  * a reverse lookup, either because the client asked for it
899  * or because the DNS lookup hit a timeout.  Do the numeric
900  * conversion and invoke the callback.
901  *
902  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
903  */
904 static void
905 numeric_reverse (void *cls)
906 {
907   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
908   char *result;
909
910   rh->task = NULL;
911   result = no_resolve (rh->af,
912                        &rh[1],
913                        rh->data_len);
914   LOG (GNUNET_ERROR_TYPE_DEBUG,
915        "Resolver returns `%s'.\n",
916        result);
917   if (NULL != result)
918   {
919     rh->name_callback (rh->cls,
920                        result);
921     GNUNET_free (result);
922   }
923   rh->name_callback (rh->cls,
924                      NULL);
925   if (NULL != rh->task)
926   {
927     GNUNET_SCHEDULER_cancel (rh->task);
928     rh->task = NULL;
929   }
930   GNUNET_free (rh);
931 }
932
933
934 /**
935  * Get an IP address as a string.
936  *
937  * @param sa host address
938  * @param salen length of host address in @a sa
939  * @param do_resolve use #GNUNET_NO to return numeric hostname
940  * @param timeout how long to try resolving
941  * @param callback function to call with hostnames
942  *        last callback is NULL when finished
943  * @param cls closure for @a callback
944  * @return handle that can be used to cancel the request
945  */
946 struct GNUNET_RESOLVER_RequestHandle *
947 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
948                               socklen_t salen,
949                               int do_resolve,
950                               struct GNUNET_TIME_Relative timeout,
951                               GNUNET_RESOLVER_HostnameCallback callback,
952                               void *cls)
953 {
954   struct GNUNET_RESOLVER_RequestHandle *rh;
955   size_t ip_len;
956   const void *ip;
957
958   if (GNUNET_OK != check_config ())
959   {
960     LOG (GNUNET_ERROR_TYPE_ERROR,
961          _("Resolver not configured correctly.\n"));
962     return NULL;
963   }
964
965   switch (sa->sa_family)
966   {
967   case AF_INET:
968     GNUNET_assert (salen == sizeof (struct sockaddr_in));
969     ip_len = sizeof (struct in_addr);
970     ip = &((const struct sockaddr_in*)sa)->sin_addr;
971     break;
972   case AF_INET6:
973     GNUNET_assert (salen == sizeof (struct sockaddr_in6));
974     ip_len = sizeof (struct in6_addr);
975     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
976     break;
977   default:
978     GNUNET_break (0);
979     return NULL;
980   }
981   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
982   rh->name_callback = callback;
983   rh->cls = cls;
984   rh->af = sa->sa_family;
985   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
986   memcpy (&rh[1],
987           ip,
988           ip_len);
989   rh->data_len = ip_len;
990   rh->direction = GNUNET_YES;
991   rh->received_response = GNUNET_NO;
992   if (GNUNET_NO == do_resolve)
993   {
994     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
995                                          rh);
996     return rh;
997   }
998   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
999                                            &handle_lookup_timeout,
1000                                            rh);
1001   GNUNET_CONTAINER_DLL_insert_tail (req_head,
1002                                     req_tail,
1003                                     rh);
1004   rh->was_queued = GNUNET_YES;
1005   if (NULL != s_task)
1006   {
1007     GNUNET_SCHEDULER_cancel (s_task);
1008     s_task = NULL;
1009   }
1010   process_requests ();
1011   return rh;
1012 }
1013
1014
1015 /**
1016  * Get local fully qualified af name
1017  *
1018  * @return fqdn
1019  */
1020 char *
1021 GNUNET_RESOLVER_local_fqdn_get ()
1022 {
1023   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1024
1025   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1026   {
1027     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1028                   "gethostname");
1029     return NULL;
1030   }
1031   LOG (GNUNET_ERROR_TYPE_DEBUG,
1032        "Resolving our FQDN `%s'\n",
1033        hostname);
1034 #if HAVE_GETADDRINFO
1035   {
1036     struct addrinfo *ai;
1037     int ret;
1038     char *rval;
1039
1040     if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
1041     {
1042       LOG (GNUNET_ERROR_TYPE_ERROR,
1043            _("Could not resolve our FQDN: %s\n"),
1044            gai_strerror (ret));
1045       return NULL;
1046     }
1047     if (NULL != ai->ai_canonname)
1048       rval = GNUNET_strdup (ai->ai_canonname);
1049     else
1050       rval = GNUNET_strdup (hostname);
1051     freeaddrinfo (ai);
1052     return rval;
1053   }
1054 #elif HAVE_GETHOSTBYNAME2
1055   {
1056     struct hostent *host;
1057
1058     host = gethostbyname2 (hostname, AF_INET);
1059     if (NULL == host)
1060       host = gethostbyname2 (hostname, AF_INET6);
1061     if (NULL == host)
1062       {
1063         LOG (GNUNET_ERROR_TYPE_ERROR,
1064              _("Could not resolve our FQDN: %s\n"),
1065              hstrerror (h_errno));
1066         return NULL;
1067       }
1068     return GNUNET_strdup (host->h_name);
1069   }
1070 #elif HAVE_GETHOSTBYNAME
1071   {
1072     struct hostent *host;
1073
1074     host = gethostbyname (hostname);
1075     if (NULL == host)
1076       {
1077         LOG (GNUNET_ERROR_TYPE_ERROR,
1078              _("Could not resolve our FQDN: %s\n"),
1079              hstrerror (h_errno));
1080         return NULL;
1081       }
1082     return GNUNET_strdup (host->h_name);
1083   }
1084 #else
1085   /* fallback: just hope name is already FQDN */
1086   return GNUNET_strdup (hostname);
1087 #endif
1088 }
1089
1090
1091 /**
1092  * Looking our own hostname.
1093  *
1094  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1095  * @param timeout how long to try resolving
1096  * @param callback function to call with addresses
1097  * @param cls closure for @a callback
1098  * @return handle that can be used to cancel the request, NULL on error
1099  */
1100 struct GNUNET_RESOLVER_RequestHandle *
1101 GNUNET_RESOLVER_hostname_resolve (int af,
1102                                   struct GNUNET_TIME_Relative timeout,
1103                                   GNUNET_RESOLVER_AddressCallback callback,
1104                                   void *cls)
1105 {
1106   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1107
1108   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1109   {
1110     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1111                   "gethostname");
1112     return NULL;
1113   }
1114   LOG (GNUNET_ERROR_TYPE_DEBUG,
1115        "Resolving our hostname `%s'\n",
1116        hostname);
1117   return GNUNET_RESOLVER_ip_get (hostname,
1118                                  af,
1119                                  timeout,
1120                                  callback,
1121                                  cls);
1122 }
1123
1124
1125 /**
1126  * Cancel a request that is still pending with the resolver.
1127  * Note that a client MUST NOT cancel a request that has
1128  * been completed (i.e, the callback has been called to
1129  * signal timeout or the final result).
1130  *
1131  * @param rh handle of request to cancel
1132  */
1133 void
1134 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1135 {
1136   if (NULL != rh->task)
1137   {
1138     GNUNET_SCHEDULER_cancel (rh->task);
1139     rh->task = NULL;
1140   }
1141   if (GNUNET_NO == rh->was_transmitted)
1142   {
1143     if (GNUNET_YES == rh->was_queued)
1144       GNUNET_CONTAINER_DLL_remove (req_head,
1145                                    req_tail,
1146                                    rh);
1147     GNUNET_free (rh);
1148     return;
1149   }
1150   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1151   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1152 }
1153
1154
1155 /* end of resolver_api.c */