Merge branch 'master' of ssh://gnunet.org/gnunet
[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, "util-resolver-api", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-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   struct GNUNET_RESOLVER_RequestHandle *rh;
253
254   while (NULL != (rh = req_head))
255   {
256     GNUNET_assert (GNUNET_SYSERR == rh->was_transmitted);
257     GNUNET_CONTAINER_DLL_remove (req_head,
258                                  req_tail,
259                                  rh);
260     GNUNET_free (rh);
261   }
262   if (NULL != mq)
263   {
264     LOG (GNUNET_ERROR_TYPE_DEBUG,
265          "Disconnecting from DNS service\n");
266     GNUNET_MQ_destroy (mq);
267     mq = NULL;
268   }
269   if (NULL != r_task)
270   {
271     GNUNET_SCHEDULER_cancel (r_task);
272     r_task = NULL;
273   }
274   if (NULL != s_task)
275   {
276     GNUNET_SCHEDULER_cancel (s_task);
277     s_task = NULL;
278   }
279 }
280
281
282 /**
283  * Task executed on system shutdown.
284  */
285 static void
286 shutdown_task (void *cls)
287 {
288   s_task = NULL;
289   GNUNET_RESOLVER_disconnect ();
290   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
291 }
292
293
294 /**
295  * Consider disconnecting if we have no further requests pending.
296  */
297 static void
298 check_disconnect ()
299 {
300   struct GNUNET_RESOLVER_RequestHandle *rh;
301
302   for (rh = req_head; NULL != rh; rh = rh->next)
303     if (GNUNET_SYSERR != rh->was_transmitted)
304       return;
305   if (NULL != r_task)
306   {
307     GNUNET_SCHEDULER_cancel (r_task);
308     r_task = NULL;
309   }
310   if (NULL != s_task)
311     return;
312   s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
313                                          &shutdown_task,
314                                          NULL);
315 }
316
317
318 /**
319  * Convert IP address to string without DNS resolution.
320  *
321  * @param af address family
322  * @param ip the address
323  * @param ip_len number of bytes in @a ip
324  * @return address as a string, NULL on error
325  */
326 static char *
327 no_resolve (int af,
328             const void *ip,
329             socklen_t ip_len)
330 {
331   char buf[INET6_ADDRSTRLEN];
332
333   switch (af)
334   {
335   case AF_INET:
336     if (ip_len != sizeof (struct in_addr))
337       return NULL;
338     if (NULL ==
339         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
340     {
341       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
342                     "inet_ntop");
343       return NULL;
344     }
345     break;
346   case AF_INET6:
347     if (ip_len != sizeof (struct in6_addr))
348       return NULL;
349     if (NULL ==
350         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
351     {
352       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
353                     "inet_ntop");
354       return NULL;
355     }
356     break;
357   default:
358     GNUNET_break (0);
359     return NULL;
360   }
361   return GNUNET_strdup (buf);
362 }
363
364
365 /**
366  * Adjust exponential back-off and reconnect to the service.
367  */
368 static void
369 reconnect (void);
370
371
372 /**
373  * Generic error handler, called with the appropriate error code and
374  * the same closure specified at the creation of the message queue.
375  * Not every message queue implementation supports an error handler.
376  *
377  * @param cls NULL
378  * @param error error code
379  */
380 static void
381 mq_error_handler (void *cls,
382                   enum GNUNET_MQ_Error error)
383 {
384   GNUNET_MQ_destroy (mq);
385   mq = NULL;
386   LOG (GNUNET_ERROR_TYPE_DEBUG,
387        "MQ error, reconnecting\n");
388   reconnect ();
389 }
390
391
392 /**
393  * Process pending requests to the resolver.
394  */
395 static void
396 process_requests ()
397 {
398   struct GNUNET_RESOLVER_GetMessage *msg;
399   struct GNUNET_MQ_Envelope *env;
400   struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
401
402   if (NULL == mq)
403   {
404     reconnect ();
405     return;
406   }
407   if (NULL == rh)
408   {
409     /* nothing to do, release socket really soon if there is nothing
410      * else happening... */
411     s_task =
412       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
413                                     &shutdown_task,
414                                     NULL);
415     return;
416   }
417   if (GNUNET_NO != rh->was_transmitted)
418     return;                     /* waiting for reply */
419   env = GNUNET_MQ_msg_extra (msg,
420                              rh->data_len,
421                              GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
422   msg->direction = htonl (rh->direction);
423   msg->af = htonl (rh->af);
424   GNUNET_memcpy (&msg[1],
425           &rh[1],
426           rh->data_len);
427   LOG (GNUNET_ERROR_TYPE_DEBUG,
428        "Transmitting DNS resolution request to DNS service\n");
429   GNUNET_MQ_send (mq,
430                   env);
431   rh->was_transmitted = GNUNET_YES;
432 }
433
434
435 /**
436  * Check validity of response with a hostname for a DNS lookup.
437  *
438  * @param cls NULL
439  * @param msg message with the hostname
440  */
441 static int
442 check_response (void *cls,
443                 const struct GNUNET_MessageHeader *msg)
444 {
445   /* implemented in #handle_response() for now */
446   return GNUNET_OK;
447 }
448
449
450 /**
451  * Check validity of response with a hostname for a DNS lookup.
452  * NOTE: right now rather messy, might want to use different
453  * message types for different response formats in the future.
454  *
455  * @param cls NULL
456  * @param msg message with the response
457  */
458 static void
459 handle_response (void *cls,
460                  const struct GNUNET_MessageHeader *msg)
461 {
462   struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
463   uint16_t size;
464   char *nret;
465
466   size = ntohs (msg->size);
467   if (size == sizeof (struct GNUNET_MessageHeader))
468   {
469     LOG (GNUNET_ERROR_TYPE_DEBUG,
470          "Received empty response from DNS service\n");
471     /* message contains not data, just header; end of replies */
472     /* check if request was canceled */
473     if (GNUNET_SYSERR != rh->was_transmitted)
474     {
475       /* no reverse lookup was successful, return IP as string */
476       if (NULL != rh->name_callback)
477       {
478         if (GNUNET_NO == rh->received_response)
479         {
480           nret = no_resolve (rh->af,
481                              &rh[1],
482                              rh->data_len);
483           rh->name_callback (rh->cls, nret);
484           GNUNET_free (nret);
485         }
486         /* finally, make termination call */
487         rh->name_callback (rh->cls,
488                            NULL);
489       }
490       if (NULL != rh->addr_callback)
491         rh->addr_callback (rh->cls,
492                            NULL,
493                            0);
494     }
495     rh->was_transmitted = GNUNET_NO;
496     GNUNET_RESOLVER_request_cancel (rh);
497     process_requests ();
498     return;
499   }
500   /* return reverse lookup results to caller */
501   if (NULL != rh->name_callback)
502   {
503     const char *hostname;
504
505     hostname = (const char *) &msg[1];
506     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
507     {
508       GNUNET_break (0);
509       if (GNUNET_SYSERR != rh->was_transmitted)
510         rh->name_callback (rh->cls,
511                            NULL);
512       rh->was_transmitted = GNUNET_NO;
513       GNUNET_RESOLVER_request_cancel (rh);
514       GNUNET_MQ_destroy (mq);
515       mq = NULL;
516       reconnect ();
517       return;
518     }
519     LOG (GNUNET_ERROR_TYPE_DEBUG,
520          "Resolver returns `%s' for IP `%s'.\n",
521          hostname,
522          GNUNET_a2s ((const void *) &rh[1],
523                      rh->data_len));
524     if (rh->was_transmitted != GNUNET_SYSERR)
525       rh->name_callback (rh->cls,
526                          hostname);
527     rh->received_response = GNUNET_YES;
528   }
529   /* return lookup results to caller */
530   if (NULL != rh->addr_callback)
531   {
532     struct sockaddr_in v4;
533     struct sockaddr_in6 v6;
534     const struct sockaddr *sa;
535     socklen_t salen;
536     const void *ip;
537     size_t ip_len;
538
539     ip = &msg[1];
540     ip_len = size - sizeof (struct GNUNET_MessageHeader);
541     if (ip_len == sizeof (struct in_addr))
542     {
543       memset (&v4, 0, sizeof (v4));
544       v4.sin_family = AF_INET;
545       v4.sin_addr = *(struct in_addr*) ip;
546 #if HAVE_SOCKADDR_IN_SIN_LEN
547       v4.sin_len = sizeof (v4);
548 #endif
549       salen = sizeof (v4);
550       sa = (const struct sockaddr *) &v4;
551     }
552     else if (ip_len == sizeof (struct in6_addr))
553     {
554       memset (&v6, 0, sizeof (v6));
555       v6.sin6_family = AF_INET6;
556       v6.sin6_addr = *(struct in6_addr*) ip;
557 #if HAVE_SOCKADDR_IN_SIN_LEN
558       v6.sin6_len = sizeof (v6);
559 #endif
560       salen = sizeof (v6);
561       sa = (const struct sockaddr *) &v6;
562     }
563     else
564     {
565       GNUNET_break (0);
566       if (GNUNET_SYSERR != rh->was_transmitted)
567         rh->addr_callback (rh->cls,
568                            NULL,
569                            0);
570       rh->was_transmitted = GNUNET_NO;
571       GNUNET_RESOLVER_request_cancel (rh);
572       GNUNET_MQ_destroy (mq);
573       mq = NULL;
574       reconnect ();
575       return;
576     }
577     LOG (GNUNET_ERROR_TYPE_DEBUG,
578          "Received IP from DNS service\n");
579     if (GNUNET_SYSERR != rh->was_transmitted)
580       rh->addr_callback (rh->cls,
581                          sa,
582                          salen);
583   }
584 }
585
586
587 /**
588  * We've been asked to lookup the address for a hostname and were
589  * given a valid numeric string.  Perform the callbacks for the
590  * numeric addresses.
591  *
592  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
593  */
594 static void
595 numeric_resolution (void *cls)
596 {
597   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
598   struct sockaddr_in v4;
599   struct sockaddr_in6 v6;
600   const char *hostname;
601
602   rh->task = NULL;
603   memset (&v4, 0, sizeof (v4));
604   v4.sin_family = AF_INET;
605 #if HAVE_SOCKADDR_IN_SIN_LEN
606   v4.sin_len = sizeof (v4);
607 #endif
608   memset (&v6, 0, sizeof (v6));
609   v6.sin6_family = AF_INET6;
610 #if HAVE_SOCKADDR_IN_SIN_LEN
611   v6.sin6_len = sizeof (v6);
612 #endif
613   hostname = (const char *) &rh[1];
614   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
615       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
616   {
617     rh->addr_callback (rh->cls,
618                        (const struct sockaddr *) &v4,
619                        sizeof (v4));
620     if ((rh->af == AF_UNSPEC) &&
621         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
622     {
623       /* this can happen on some systems IF "hostname" is "localhost" */
624       rh->addr_callback (rh->cls,
625                          (const struct sockaddr *) &v6,
626                          sizeof (v6));
627     }
628     rh->addr_callback (rh->cls,
629                        NULL,
630                        0);
631     GNUNET_free (rh);
632     return;
633   }
634   if ( ( (rh->af == AF_UNSPEC) ||
635          (rh->af == AF_INET6) ) &&
636        (1 == inet_pton (AF_INET6,
637                         hostname,
638                         &v6.sin6_addr) ) )
639   {
640     rh->addr_callback (rh->cls,
641                        (const struct sockaddr *) &v6,
642                        sizeof (v6));
643     rh->addr_callback (rh->cls,
644                        NULL,
645                        0);
646     GNUNET_free (rh);
647     return;
648   }
649   /* why are we here? this task should not have been scheduled! */
650   GNUNET_assert (0);
651   GNUNET_free (rh);
652 }
653
654
655 /**
656  * We've been asked to lookup the address for a hostname and were
657  * given a variant of "loopback".  Perform the callbacks for the
658  * respective loopback numeric addresses.
659  *
660  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
661  */
662 static void
663 loopback_resolution (void *cls)
664 {
665   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
666   struct sockaddr_in v4;
667   struct sockaddr_in6 v6;
668
669   rh->task = NULL;
670   memset (&v4, 0, sizeof (v4));
671   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
672   v4.sin_family = AF_INET;
673 #if HAVE_SOCKADDR_IN_SIN_LEN
674   v4.sin_len = sizeof (v4);
675 #endif
676   memset (&v6, 0, sizeof (v6));
677   v6.sin6_family = AF_INET6;
678 #if HAVE_SOCKADDR_IN_SIN_LEN
679   v6.sin6_len = sizeof (v6);
680 #endif
681   v6.sin6_addr = in6addr_loopback;
682   switch (rh->af)
683   {
684   case AF_INET:
685     rh->addr_callback (rh->cls,
686                        (const struct sockaddr *) &v4,
687                        sizeof (v4));
688     break;
689   case AF_INET6:
690     rh->addr_callback (rh->cls,
691                        (const struct sockaddr *) &v6,
692                        sizeof (v6));
693     break;
694   case AF_UNSPEC:
695     rh->addr_callback (rh->cls,
696                        (const struct sockaddr *) &v6,
697                        sizeof (v6));
698     rh->addr_callback (rh->cls,
699                        (const struct sockaddr *) &v4,
700                        sizeof (v4));
701
702     break;
703   default:
704     GNUNET_break (0);
705     break;
706   }
707   rh->addr_callback (rh->cls,
708                      NULL,
709                      0);
710   LOG (GNUNET_ERROR_TYPE_DEBUG,
711        "Finished resolving hostname `%s'.\n",
712        (const char *) &rh[1]);
713   GNUNET_free (rh);
714 }
715
716
717 /**
718  * Now try to reconnect to the resolver service.
719  *
720  * @param cls NULL
721  */
722 static void
723 reconnect_task (void *cls)
724 {
725   struct GNUNET_MQ_MessageHandler handlers[] = {
726     GNUNET_MQ_hd_var_size (response,
727                            GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
728                            struct GNUNET_MessageHeader,
729                            NULL),
730     GNUNET_MQ_handler_end ()
731   };
732
733   r_task = NULL;
734   if (NULL == req_head)
735     return;                     /* no work pending */
736   LOG (GNUNET_ERROR_TYPE_DEBUG,
737        "Trying to connect to DNS service\n");
738   mq = GNUNET_CLIENT_connect (resolver_cfg,
739                               "resolver",
740                               handlers,
741                               &mq_error_handler,
742                               NULL);
743   if (NULL == mq)
744   {
745     LOG (GNUNET_ERROR_TYPE_DEBUG,
746          "Failed to connect, will try again later\n");
747     reconnect ();
748     return;
749   }
750   process_requests ();
751 }
752
753
754 /**
755  * Adjust exponential back-off and reconnect to the service.
756  */
757 static void
758 reconnect ()
759 {
760   struct GNUNET_RESOLVER_RequestHandle *rh;
761
762   if (NULL != r_task)
763     return;
764   GNUNET_assert (NULL == mq);
765   if (NULL != (rh = req_head))
766   {
767     switch (rh->was_transmitted)
768     {
769     case GNUNET_NO:
770       /* nothing more to do */
771       break;
772     case GNUNET_YES:
773       /* disconnected, transmit again! */
774       rh->was_transmitted = GNUNET_NO;
775       break;
776     case GNUNET_SYSERR:
777       /* request was cancelled, remove entirely */
778       GNUNET_CONTAINER_DLL_remove (req_head,
779                                    req_tail,
780                                    rh);
781       GNUNET_free (rh);
782       check_disconnect ();
783       break;
784     default:
785       GNUNET_assert (0);
786       break;
787     }
788   }
789   LOG (GNUNET_ERROR_TYPE_DEBUG,
790        "Will try to connect to DNS service in %s\n",
791        GNUNET_STRINGS_relative_time_to_string (backoff,
792                                                GNUNET_YES));
793   GNUNET_assert (NULL != resolver_cfg);
794   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
795                                          &reconnect_task,
796                                          NULL);
797   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
798 }
799
800
801 /**
802  * A DNS resolution timed out. Notify the application.
803  *
804  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
805  */
806 static void
807 handle_lookup_timeout (void *cls)
808 {
809   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
810
811   rh->task = NULL;
812   if (GNUNET_NO == rh->direction)
813   {
814     LOG (GNUNET_ERROR_TYPE_INFO,
815          _("Timeout trying to resolve hostname `%s'.\n"),
816          (const char *) &rh[1]);
817     if (NULL != rh->addr_callback)
818       rh->addr_callback (rh->cls,
819                          NULL,
820                          0);
821   }
822   else
823   {
824     char buf[INET6_ADDRSTRLEN];
825
826     LOG (GNUNET_ERROR_TYPE_INFO,
827          _("Timeout trying to resolve IP address `%s'.\n"),
828          inet_ntop (rh->af,
829                     (const void *) &rh[1],
830                     buf,
831                     sizeof(buf)));
832     if (GNUNET_NO == rh->received_response)
833     {
834       char *nret;
835
836       nret = no_resolve (rh->af,
837                          &rh[1],
838                          rh->data_len);
839       if (NULL != rh->name_callback)
840         rh->name_callback (rh->cls, nret);
841       GNUNET_free (nret);
842     }
843     /* finally, make termination call */
844     if (NULL != rh->name_callback)
845       rh->name_callback (rh->cls,
846                          NULL);
847   }
848   rh->was_transmitted = GNUNET_NO;
849   GNUNET_RESOLVER_request_cancel (rh);
850   process_requests ();
851 }
852
853
854 /**
855  * Convert a string to one or more IP addresses.
856  *
857  * @param hostname the hostname to resolve
858  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
859  * @param callback function to call with addresses
860  * @param callback_cls closure for @a callback
861  * @param timeout how long to try resolving
862  * @return handle that can be used to cancel the request, NULL on error
863  */
864 struct GNUNET_RESOLVER_RequestHandle *
865 GNUNET_RESOLVER_ip_get (const char *hostname,
866                         int af,
867                         struct GNUNET_TIME_Relative timeout,
868                         GNUNET_RESOLVER_AddressCallback callback,
869                         void *callback_cls)
870 {
871   struct GNUNET_RESOLVER_RequestHandle *rh;
872   size_t slen;
873   unsigned int i;
874   struct in_addr v4;
875   struct in6_addr v6;
876
877   slen = strlen (hostname) + 1;
878   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
879       GNUNET_MAX_MESSAGE_SIZE)
880   {
881     GNUNET_break (0);
882     return NULL;
883   }
884   LOG (GNUNET_ERROR_TYPE_DEBUG,
885        "Trying to resolve hostname `%s'.\n",
886        hostname);
887   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
888   rh->af = af;
889   rh->addr_callback = callback;
890   rh->cls = callback_cls;
891   GNUNET_memcpy (&rh[1],
892                  hostname,
893                  slen);
894   rh->data_len = slen;
895   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
896   rh->direction = GNUNET_NO;
897   /* first, check if this is a numeric address */
898   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
899        ((af == AF_INET) || (af == AF_UNSPEC))) ||
900       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
901        ((af == AF_INET6) || (af == AF_UNSPEC))))
902   {
903     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
904                                          rh);
905     return rh;
906   }
907   /* then, check if this is a loopback address */
908   i = 0;
909   while (NULL != loopback[i])
910     if (0 == strcasecmp (loopback[i++],
911                          hostname))
912     {
913       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
914                                            rh);
915       return rh;
916     }
917   if (GNUNET_OK != check_config ())
918   {
919     GNUNET_free (rh);
920     return NULL;
921   }
922   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
923                                            &handle_lookup_timeout,
924                                            rh);
925   GNUNET_CONTAINER_DLL_insert_tail (req_head,
926                                     req_tail,
927                                     rh);
928   rh->was_queued = GNUNET_YES;
929   if (NULL != s_task)
930   {
931     GNUNET_SCHEDULER_cancel (s_task);
932     s_task = NULL;
933   }
934   process_requests ();
935   return rh;
936 }
937
938
939 /**
940  * We've been asked to convert an address to a string without
941  * a reverse lookup, either because the client asked for it
942  * or because the DNS lookup hit a timeout.  Do the numeric
943  * conversion and invoke the callback.
944  *
945  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
946  */
947 static void
948 numeric_reverse (void *cls)
949 {
950   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
951   char *result;
952
953   rh->task = NULL;
954   result = no_resolve (rh->af,
955                        &rh[1],
956                        rh->data_len);
957   LOG (GNUNET_ERROR_TYPE_DEBUG,
958        "Resolver returns `%s'.\n",
959        result);
960   if (NULL != result)
961   {
962     rh->name_callback (rh->cls,
963                        result);
964     GNUNET_free (result);
965   }
966   rh->name_callback (rh->cls,
967                      NULL);
968   if (NULL != rh->task)
969   {
970     GNUNET_SCHEDULER_cancel (rh->task);
971     rh->task = NULL;
972   }
973   GNUNET_free (rh);
974 }
975
976
977 /**
978  * Get an IP address as a string.
979  *
980  * @param sa host address
981  * @param salen length of host address in @a sa
982  * @param do_resolve use #GNUNET_NO to return numeric hostname
983  * @param timeout how long to try resolving
984  * @param callback function to call with hostnames
985  *        last callback is NULL when finished
986  * @param cls closure for @a callback
987  * @return handle that can be used to cancel the request
988  */
989 struct GNUNET_RESOLVER_RequestHandle *
990 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
991                               socklen_t salen,
992                               int do_resolve,
993                               struct GNUNET_TIME_Relative timeout,
994                               GNUNET_RESOLVER_HostnameCallback callback,
995                               void *cls)
996 {
997   struct GNUNET_RESOLVER_RequestHandle *rh;
998   size_t ip_len;
999   const void *ip;
1000
1001   if (GNUNET_OK != check_config ())
1002   {
1003     LOG (GNUNET_ERROR_TYPE_ERROR,
1004          _("Resolver not configured correctly.\n"));
1005     return NULL;
1006   }
1007
1008   switch (sa->sa_family)
1009   {
1010   case AF_INET:
1011     GNUNET_assert (salen == sizeof (struct sockaddr_in));
1012     ip_len = sizeof (struct in_addr);
1013     ip = &((const struct sockaddr_in*)sa)->sin_addr;
1014     break;
1015   case AF_INET6:
1016     GNUNET_assert (salen == sizeof (struct sockaddr_in6));
1017     ip_len = sizeof (struct in6_addr);
1018     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
1019     break;
1020   default:
1021     GNUNET_break (0);
1022     return NULL;
1023   }
1024   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
1025   rh->name_callback = callback;
1026   rh->cls = cls;
1027   rh->af = sa->sa_family;
1028   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1029   GNUNET_memcpy (&rh[1],
1030           ip,
1031           ip_len);
1032   rh->data_len = ip_len;
1033   rh->direction = GNUNET_YES;
1034   rh->received_response = GNUNET_NO;
1035   if (GNUNET_NO == do_resolve)
1036   {
1037     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
1038                                          rh);
1039     return rh;
1040   }
1041   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1042                                            &handle_lookup_timeout,
1043                                            rh);
1044   GNUNET_CONTAINER_DLL_insert_tail (req_head,
1045                                     req_tail,
1046                                     rh);
1047   rh->was_queued = GNUNET_YES;
1048   if (NULL != s_task)
1049   {
1050     GNUNET_SCHEDULER_cancel (s_task);
1051     s_task = NULL;
1052   }
1053   process_requests ();
1054   return rh;
1055 }
1056
1057
1058 /**
1059  * Get local fully qualified af name
1060  *
1061  * @return fqdn
1062  */
1063 char *
1064 GNUNET_RESOLVER_local_fqdn_get ()
1065 {
1066   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1067
1068   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1069   {
1070     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1071                   "gethostname");
1072     return NULL;
1073   }
1074   LOG (GNUNET_ERROR_TYPE_DEBUG,
1075        "Resolving our FQDN `%s'\n",
1076        hostname);
1077 #if HAVE_GETADDRINFO
1078   {
1079     struct addrinfo *ai;
1080     int ret;
1081     char *rval;
1082
1083     if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
1084     {
1085       LOG (GNUNET_ERROR_TYPE_ERROR,
1086            _("Could not resolve our FQDN: %s\n"),
1087            gai_strerror (ret));
1088       return NULL;
1089     }
1090     if (NULL != ai->ai_canonname)
1091       rval = GNUNET_strdup (ai->ai_canonname);
1092     else
1093       rval = GNUNET_strdup (hostname);
1094     freeaddrinfo (ai);
1095     return rval;
1096   }
1097 #elif HAVE_GETHOSTBYNAME2
1098   {
1099     struct hostent *host;
1100
1101     host = gethostbyname2 (hostname, AF_INET);
1102     if (NULL == host)
1103       host = gethostbyname2 (hostname, AF_INET6);
1104     if (NULL == host)
1105       {
1106         LOG (GNUNET_ERROR_TYPE_ERROR,
1107              _("Could not resolve our FQDN: %s\n"),
1108              hstrerror (h_errno));
1109         return NULL;
1110       }
1111     return GNUNET_strdup (host->h_name);
1112   }
1113 #elif HAVE_GETHOSTBYNAME
1114   {
1115     struct hostent *host;
1116
1117     host = gethostbyname (hostname);
1118     if (NULL == host)
1119       {
1120         LOG (GNUNET_ERROR_TYPE_ERROR,
1121              _("Could not resolve our FQDN: %s\n"),
1122              hstrerror (h_errno));
1123         return NULL;
1124       }
1125     return GNUNET_strdup (host->h_name);
1126   }
1127 #else
1128   /* fallback: just hope name is already FQDN */
1129   return GNUNET_strdup (hostname);
1130 #endif
1131 }
1132
1133
1134 /**
1135  * Looking our own hostname.
1136  *
1137  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1138  * @param timeout how long to try resolving
1139  * @param callback function to call with addresses
1140  * @param cls closure for @a callback
1141  * @return handle that can be used to cancel the request, NULL on error
1142  */
1143 struct GNUNET_RESOLVER_RequestHandle *
1144 GNUNET_RESOLVER_hostname_resolve (int af,
1145                                   struct GNUNET_TIME_Relative timeout,
1146                                   GNUNET_RESOLVER_AddressCallback callback,
1147                                   void *cls)
1148 {
1149   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1150
1151   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1152   {
1153     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1154                   "gethostname");
1155     return NULL;
1156   }
1157   LOG (GNUNET_ERROR_TYPE_DEBUG,
1158        "Resolving our hostname `%s'\n",
1159        hostname);
1160   return GNUNET_RESOLVER_ip_get (hostname,
1161                                  af,
1162                                  timeout,
1163                                  callback,
1164                                  cls);
1165 }
1166
1167
1168 /**
1169  * Cancel a request that is still pending with the resolver.
1170  * Note that a client MUST NOT cancel a request that has
1171  * been completed (i.e, the callback has been called to
1172  * signal timeout or the final result).
1173  *
1174  * @param rh handle of request to cancel
1175  */
1176 void
1177 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1178 {
1179   if (GNUNET_NO == rh->direction)
1180     LOG (GNUNET_ERROR_TYPE_DEBUG,
1181          "Asked to cancel request to resolve hostname `%s'.\n",
1182          (const char *) &rh[1]);
1183   if (NULL != rh->task)
1184   {
1185     GNUNET_SCHEDULER_cancel (rh->task);
1186     rh->task = NULL;
1187   }
1188   if (GNUNET_NO == rh->was_transmitted)
1189   {
1190     if (GNUNET_YES == rh->was_queued)
1191       GNUNET_CONTAINER_DLL_remove (req_head,
1192                                    req_tail,
1193                                    rh);
1194     GNUNET_free (rh);
1195     check_disconnect ();
1196     return;
1197   }
1198   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1199   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1200   check_disconnect ();
1201 }
1202
1203
1204 /* end of resolver_api.c */