-towards IdP2
[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 #if !defined(GNUNET_CULL_LOGGING)
825     char buf[INET6_ADDRSTRLEN];
826
827     LOG (GNUNET_ERROR_TYPE_INFO,
828          _("Timeout trying to resolve IP address `%s'.\n"),
829          inet_ntop (rh->af,
830                     (const void *) &rh[1],
831                     buf,
832                     sizeof(buf)));
833 #endif
834     if (GNUNET_NO == rh->received_response)
835     {
836       char *nret;
837
838       nret = no_resolve (rh->af,
839                          &rh[1],
840                          rh->data_len);
841       if (NULL != rh->name_callback)
842         rh->name_callback (rh->cls, nret);
843       GNUNET_free (nret);
844     }
845     /* finally, make termination call */
846     if (NULL != rh->name_callback)
847       rh->name_callback (rh->cls,
848                          NULL);
849   }
850   rh->was_transmitted = GNUNET_NO;
851   GNUNET_RESOLVER_request_cancel (rh);
852   process_requests ();
853 }
854
855
856 /**
857  * Convert a string to one or more IP addresses.
858  *
859  * @param hostname the hostname to resolve
860  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
861  * @param callback function to call with addresses
862  * @param callback_cls closure for @a callback
863  * @param timeout how long to try resolving
864  * @return handle that can be used to cancel the request, NULL on error
865  */
866 struct GNUNET_RESOLVER_RequestHandle *
867 GNUNET_RESOLVER_ip_get (const char *hostname,
868                         int af,
869                         struct GNUNET_TIME_Relative timeout,
870                         GNUNET_RESOLVER_AddressCallback callback,
871                         void *callback_cls)
872 {
873   struct GNUNET_RESOLVER_RequestHandle *rh;
874   size_t slen;
875   unsigned int i;
876   struct in_addr v4;
877   struct in6_addr v6;
878
879   slen = strlen (hostname) + 1;
880   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
881       GNUNET_MAX_MESSAGE_SIZE)
882   {
883     GNUNET_break (0);
884     return NULL;
885   }
886   LOG (GNUNET_ERROR_TYPE_DEBUG,
887        "Trying to resolve hostname `%s'.\n",
888        hostname);
889   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
890   rh->af = af;
891   rh->addr_callback = callback;
892   rh->cls = callback_cls;
893   GNUNET_memcpy (&rh[1],
894                  hostname,
895                  slen);
896   rh->data_len = slen;
897   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
898   rh->direction = GNUNET_NO;
899   /* first, check if this is a numeric address */
900   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
901        ((af == AF_INET) || (af == AF_UNSPEC))) ||
902       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
903        ((af == AF_INET6) || (af == AF_UNSPEC))))
904   {
905     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
906                                          rh);
907     return rh;
908   }
909   /* then, check if this is a loopback address */
910   i = 0;
911   while (NULL != loopback[i])
912     if (0 == strcasecmp (loopback[i++],
913                          hostname))
914     {
915       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
916                                            rh);
917       return rh;
918     }
919   if (GNUNET_OK != check_config ())
920   {
921     GNUNET_free (rh);
922     return NULL;
923   }
924   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
925                                            &handle_lookup_timeout,
926                                            rh);
927   GNUNET_CONTAINER_DLL_insert_tail (req_head,
928                                     req_tail,
929                                     rh);
930   rh->was_queued = GNUNET_YES;
931   if (NULL != s_task)
932   {
933     GNUNET_SCHEDULER_cancel (s_task);
934     s_task = NULL;
935   }
936   process_requests ();
937   return rh;
938 }
939
940
941 /**
942  * We've been asked to convert an address to a string without
943  * a reverse lookup, either because the client asked for it
944  * or because the DNS lookup hit a timeout.  Do the numeric
945  * conversion and invoke the callback.
946  *
947  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
948  */
949 static void
950 numeric_reverse (void *cls)
951 {
952   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
953   char *result;
954
955   rh->task = NULL;
956   result = no_resolve (rh->af,
957                        &rh[1],
958                        rh->data_len);
959   LOG (GNUNET_ERROR_TYPE_DEBUG,
960        "Resolver returns `%s'.\n",
961        result);
962   if (NULL != result)
963   {
964     rh->name_callback (rh->cls,
965                        result);
966     GNUNET_free (result);
967   }
968   rh->name_callback (rh->cls,
969                      NULL);
970   if (NULL != rh->task)
971   {
972     GNUNET_SCHEDULER_cancel (rh->task);
973     rh->task = NULL;
974   }
975   GNUNET_free (rh);
976 }
977
978
979 /**
980  * Get an IP address as a string.
981  *
982  * @param sa host address
983  * @param salen length of host address in @a sa
984  * @param do_resolve use #GNUNET_NO to return numeric hostname
985  * @param timeout how long to try resolving
986  * @param callback function to call with hostnames
987  *        last callback is NULL when finished
988  * @param cls closure for @a callback
989  * @return handle that can be used to cancel the request
990  */
991 struct GNUNET_RESOLVER_RequestHandle *
992 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
993                               socklen_t salen,
994                               int do_resolve,
995                               struct GNUNET_TIME_Relative timeout,
996                               GNUNET_RESOLVER_HostnameCallback callback,
997                               void *cls)
998 {
999   struct GNUNET_RESOLVER_RequestHandle *rh;
1000   size_t ip_len;
1001   const void *ip;
1002
1003   if (GNUNET_OK != check_config ())
1004   {
1005     LOG (GNUNET_ERROR_TYPE_ERROR,
1006          _("Resolver not configured correctly.\n"));
1007     return NULL;
1008   }
1009
1010   switch (sa->sa_family)
1011   {
1012   case AF_INET:
1013     GNUNET_assert (salen == sizeof (struct sockaddr_in));
1014     ip_len = sizeof (struct in_addr);
1015     ip = &((const struct sockaddr_in*)sa)->sin_addr;
1016     break;
1017   case AF_INET6:
1018     GNUNET_assert (salen == sizeof (struct sockaddr_in6));
1019     ip_len = sizeof (struct in6_addr);
1020     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
1021     break;
1022   default:
1023     GNUNET_break (0);
1024     return NULL;
1025   }
1026   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
1027   rh->name_callback = callback;
1028   rh->cls = cls;
1029   rh->af = sa->sa_family;
1030   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1031   GNUNET_memcpy (&rh[1],
1032           ip,
1033           ip_len);
1034   rh->data_len = ip_len;
1035   rh->direction = GNUNET_YES;
1036   rh->received_response = GNUNET_NO;
1037   if (GNUNET_NO == do_resolve)
1038   {
1039     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
1040                                          rh);
1041     return rh;
1042   }
1043   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1044                                            &handle_lookup_timeout,
1045                                            rh);
1046   GNUNET_CONTAINER_DLL_insert_tail (req_head,
1047                                     req_tail,
1048                                     rh);
1049   rh->was_queued = GNUNET_YES;
1050   if (NULL != s_task)
1051   {
1052     GNUNET_SCHEDULER_cancel (s_task);
1053     s_task = NULL;
1054   }
1055   process_requests ();
1056   return rh;
1057 }
1058
1059
1060 /**
1061  * Get local fully qualified af name
1062  *
1063  * @return fqdn
1064  */
1065 char *
1066 GNUNET_RESOLVER_local_fqdn_get ()
1067 {
1068   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1069
1070   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1071   {
1072     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1073                   "gethostname");
1074     return NULL;
1075   }
1076   LOG (GNUNET_ERROR_TYPE_DEBUG,
1077        "Resolving our FQDN `%s'\n",
1078        hostname);
1079 #if HAVE_GETADDRINFO
1080   {
1081     struct addrinfo *ai;
1082     int ret;
1083     char *rval;
1084
1085     if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
1086     {
1087       LOG (GNUNET_ERROR_TYPE_ERROR,
1088            _("Could not resolve our FQDN: %s\n"),
1089            gai_strerror (ret));
1090       return NULL;
1091     }
1092     if (NULL != ai->ai_canonname)
1093       rval = GNUNET_strdup (ai->ai_canonname);
1094     else
1095       rval = GNUNET_strdup (hostname);
1096     freeaddrinfo (ai);
1097     return rval;
1098   }
1099 #elif HAVE_GETHOSTBYNAME2
1100   {
1101     struct hostent *host;
1102
1103     host = gethostbyname2 (hostname, AF_INET);
1104     if (NULL == host)
1105       host = gethostbyname2 (hostname, AF_INET6);
1106     if (NULL == host)
1107       {
1108         LOG (GNUNET_ERROR_TYPE_ERROR,
1109              _("Could not resolve our FQDN: %s\n"),
1110              hstrerror (h_errno));
1111         return NULL;
1112       }
1113     return GNUNET_strdup (host->h_name);
1114   }
1115 #elif HAVE_GETHOSTBYNAME
1116   {
1117     struct hostent *host;
1118
1119     host = gethostbyname (hostname);
1120     if (NULL == host)
1121       {
1122         LOG (GNUNET_ERROR_TYPE_ERROR,
1123              _("Could not resolve our FQDN: %s\n"),
1124              hstrerror (h_errno));
1125         return NULL;
1126       }
1127     return GNUNET_strdup (host->h_name);
1128   }
1129 #else
1130   /* fallback: just hope name is already FQDN */
1131   return GNUNET_strdup (hostname);
1132 #endif
1133 }
1134
1135
1136 /**
1137  * Looking our own hostname.
1138  *
1139  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1140  * @param timeout how long to try resolving
1141  * @param callback function to call with addresses
1142  * @param cls closure for @a callback
1143  * @return handle that can be used to cancel the request, NULL on error
1144  */
1145 struct GNUNET_RESOLVER_RequestHandle *
1146 GNUNET_RESOLVER_hostname_resolve (int af,
1147                                   struct GNUNET_TIME_Relative timeout,
1148                                   GNUNET_RESOLVER_AddressCallback callback,
1149                                   void *cls)
1150 {
1151   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1152
1153   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1154   {
1155     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1156                   "gethostname");
1157     return NULL;
1158   }
1159   LOG (GNUNET_ERROR_TYPE_DEBUG,
1160        "Resolving our hostname `%s'\n",
1161        hostname);
1162   return GNUNET_RESOLVER_ip_get (hostname,
1163                                  af,
1164                                  timeout,
1165                                  callback,
1166                                  cls);
1167 }
1168
1169
1170 /**
1171  * Cancel a request that is still pending with the resolver.
1172  * Note that a client MUST NOT cancel a request that has
1173  * been completed (i.e, the callback has been called to
1174  * signal timeout or the final result).
1175  *
1176  * @param rh handle of request to cancel
1177  */
1178 void
1179 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1180 {
1181   if (GNUNET_NO == rh->direction)
1182     LOG (GNUNET_ERROR_TYPE_DEBUG,
1183          "Asked to cancel request to resolve hostname `%s'.\n",
1184          (const char *) &rh[1]);
1185   if (NULL != rh->task)
1186   {
1187     GNUNET_SCHEDULER_cancel (rh->task);
1188     rh->task = NULL;
1189   }
1190   if (GNUNET_NO == rh->was_transmitted)
1191   {
1192     if (GNUNET_YES == rh->was_queued)
1193       GNUNET_CONTAINER_DLL_remove (req_head,
1194                                    req_tail,
1195                                    rh);
1196     GNUNET_free (rh);
1197     check_disconnect ();
1198     return;
1199   }
1200   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1201   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1202   check_disconnect ();
1203 }
1204
1205
1206 /* end of resolver_api.c */