fix for resolver timeout issues
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2011 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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_getopt_lib.h"
28 #include "gnunet_os_lib.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_container_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_server_lib.h"
34 #include "resolver.h"
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
37
38 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
39
40 /**
41  * Maximum supported length for a hostname
42  */
43 #define MAX_HOSTNAME 1024
44
45
46 /**
47  * Possible hostnames for "loopback".
48  */
49 static const char *loopback[] = {
50   "localhost",
51   "ip6-localnet",
52   NULL
53 };
54
55
56 /**
57  * Configuration.
58  */
59 static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
60
61 /**
62  * Our connection to the resolver service, created on-demand, but then
63  * persists until error or shutdown.
64  */
65 static struct GNUNET_CLIENT_Connection *client;
66
67 /**
68  * Head of DLL of requests.
69  */
70 static struct GNUNET_RESOLVER_RequestHandle *req_head;
71
72 /**
73  * Tail of DLL of requests.
74  */
75 static struct GNUNET_RESOLVER_RequestHandle *req_tail;
76
77 /**
78  * How long should we wait to reconnect?
79  */
80 static struct GNUNET_TIME_Relative backoff;
81
82 /**
83  * Task for reconnecting.
84  */
85 static GNUNET_SCHEDULER_TaskIdentifier r_task;
86
87 /**
88  * Task ID of shutdown task; only present while we have a
89  * connection to the resolver service.
90  */
91 static GNUNET_SCHEDULER_TaskIdentifier s_task;
92
93
94 /**
95  * Handle to a request given to the resolver.  Can be used to cancel
96  * the request prior to the timeout or successful execution.  Also
97  * used to track our internal state for the request.
98  */
99 struct GNUNET_RESOLVER_RequestHandle
100 {
101
102   /**
103    * Next entry in DLL of requests.
104    */
105   struct GNUNET_RESOLVER_RequestHandle *next;
106
107   /**
108    * Previous entry in DLL of requests.
109    */
110   struct GNUNET_RESOLVER_RequestHandle *prev;
111
112   /**
113    * Callback if this is an name resolution request,
114    * otherwise NULL.
115    */
116   GNUNET_RESOLVER_AddressCallback addr_callback;
117
118   /**
119    * Callback if this is a reverse lookup request,
120    * otherwise NULL.
121    */
122   GNUNET_RESOLVER_HostnameCallback name_callback;
123
124   /**
125    * Closure for the respective "callback".
126    */
127   void *cls;
128
129   /**
130    * When should this request time out?
131    */
132   struct GNUNET_TIME_Absolute timeout;
133
134   /**
135    * Task handle for numeric lookups.
136    */
137   GNUNET_SCHEDULER_TaskIdentifier task;
138
139   /**
140    * Desired address family.
141    */
142   int af;
143
144   /**
145    * Has this request been transmitted to the service?
146    * GNUNET_YES if transmitted
147    * GNUNET_YES if not transmitted
148    * GNUNET_SYSERR when request was canceled
149    */
150   int was_transmitted;
151
152   /**
153    * Did we add this request to the queue?
154    */
155   int was_queued;
156
157   /**
158    * Desired direction (IP to name or name to IP)
159    */
160   int direction;
161
162   /**
163    * GNUNET_YES if a response was received
164    */
165   int received_response;
166
167   /**
168    * Length of the data that follows this struct.
169    */
170   size_t data_len;
171 };
172
173
174 /**
175  * Check that the resolver service runs on localhost
176  * (or equivalent).
177  */
178 static void
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, "resolver",
199                                              "HOSTNAME", &hostname))
200   {
201     LOG (GNUNET_ERROR_TYPE_ERROR,
202          _("Must specify `%s' for `%s' in configuration!\n"), "HOSTNAME",
203          "resolver");
204     GNUNET_assert (0);
205   }
206   if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
207       (1 != inet_pton (AF_INET6, hostname, &v6)))
208   {
209     GNUNET_free (hostname);
210     return;
211   }
212   i = 0;
213   while (loopback[i] != NULL)
214     if (0 == strcasecmp (loopback[i++], hostname))
215     {
216       GNUNET_free (hostname);
217       return;
218     }
219   LOG (GNUNET_ERROR_TYPE_ERROR,
220        _
221        ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
222        "localhost", "HOSTNAME", "resolver");
223   GNUNET_free (hostname);
224   GNUNET_assert (0);
225 }
226
227
228 /**
229  * Create the connection to the resolver service.
230  *
231  * @param cfg configuration to use
232  */
233 void
234 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
235 {
236   GNUNET_assert (NULL != cfg);
237   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
238   resolver_cfg = cfg;
239   check_config ();
240 }
241
242
243 /**
244  * Destroy the connection to the resolver service.
245  */
246 void
247 GNUNET_RESOLVER_disconnect ()
248 {
249   GNUNET_assert (NULL == req_head);
250   GNUNET_assert (NULL == req_tail);
251   if (NULL != client)
252   {
253     LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
254     GNUNET_CLIENT_disconnect (client);
255     client = NULL;
256   }
257   if (r_task != GNUNET_SCHEDULER_NO_TASK)
258   {
259     GNUNET_SCHEDULER_cancel (r_task);
260     r_task = GNUNET_SCHEDULER_NO_TASK;
261   }
262   if (s_task != GNUNET_SCHEDULER_NO_TASK)
263   {
264     GNUNET_SCHEDULER_cancel (s_task);
265     s_task = GNUNET_SCHEDULER_NO_TASK;
266   }
267 }
268
269
270 /**
271  * Convert IP address to string without DNS resolution.
272  *
273  * @param af address family
274  * @param ip the address
275  * @param ip_len number of bytes in ip
276  * @return address as a string, NULL on error
277  */
278 static char *
279 no_resolve (int af,
280             const void *ip, socklen_t ip_len)
281 {
282   char buf[INET6_ADDRSTRLEN];
283
284   switch (af)
285   {
286   case AF_INET:
287     if (ip_len != sizeof (struct in_addr))
288       return NULL;
289     if (NULL ==
290         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
291     {
292       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
293       return NULL;
294     }
295     break;
296   case AF_INET6:
297     if (ip_len != sizeof (struct in6_addr))
298       return NULL;
299     if (NULL ==
300         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
301     {
302       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
303       return NULL;
304     }
305     break;
306   default:
307     GNUNET_break (0);
308     return NULL;
309   }
310   return GNUNET_strdup (buf);
311 }
312
313
314 /**
315  * Adjust exponential back-off and reconnect to the service.
316  */
317 static void
318 reconnect ();
319
320
321 /**
322  * Process pending requests to the resolver.
323  */
324 static void
325 process_requests ();
326
327
328 /**
329  * Process response with a hostname for a DNS lookup.
330  *
331  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
332  * @param msg message with the hostname, NULL on error
333  */
334 static void
335 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
336 {
337   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
338   uint16_t size;
339
340   LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
341   if (msg == NULL)
342   {
343     char buf[INET6_ADDRSTRLEN];
344
345     if (NULL != rh->name_callback)
346       LOG (GNUNET_ERROR_TYPE_INFO,
347            _("Timeout trying to resolve IP address `%s'.\n"),
348            inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
349     else
350       LOG (GNUNET_ERROR_TYPE_INFO,
351            _("Timeout trying to resolve hostname `%s'.\n"),
352            (const char *) &rh[1]);
353     /* check if request was canceled */
354     if (rh->was_transmitted != GNUNET_SYSERR)
355     {
356       if (NULL != rh->name_callback)
357       {
358         /* no reverse lookup was successful, return ip as string */
359         if (rh->received_response == GNUNET_NO)
360         {
361           rh->name_callback (rh->cls, no_resolve (rh->af, &rh[1], rh->data_len));
362           rh->name_callback (rh->cls, NULL);
363         }
364         /* at least one reverse lookup was successful */
365         else
366           rh->name_callback (rh->cls, NULL);
367       }
368       if (NULL != rh->addr_callback)
369         rh->addr_callback (rh->cls, NULL, 0);
370     }
371     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
372     GNUNET_free (rh);
373     GNUNET_CLIENT_disconnect (client);
374     client = NULL;
375     reconnect ();
376     return;
377   }
378   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
379   {
380     GNUNET_break (0);
381     GNUNET_CLIENT_disconnect (client);
382     client = NULL;
383     reconnect ();
384     return;
385   }
386   size = ntohs (msg->size);
387   /* message contains not data, just header */
388   if (size == sizeof (struct GNUNET_MessageHeader))
389   {
390     /* check if request was canceled */
391     if (rh->was_transmitted != GNUNET_SYSERR)
392     {
393       if (NULL != rh->name_callback)
394         rh->name_callback (rh->cls, NULL);
395       if (NULL != rh->addr_callback)
396         rh->addr_callback (rh->cls, NULL, 0);
397     }
398     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
399     GNUNET_free (rh);
400     process_requests ();
401     return;
402   }
403   /* return reverse lookup results to caller */
404   if (NULL != rh->name_callback)
405   {
406     const char *hostname;
407
408     hostname = (const char *) &msg[1];
409     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
410     {
411       GNUNET_break (0);
412       if (rh->was_transmitted != GNUNET_SYSERR)
413         rh->name_callback (rh->cls, NULL);
414       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
415       GNUNET_free (rh);
416       GNUNET_CLIENT_disconnect (client);
417       client = NULL;
418       reconnect ();
419       return;
420     }
421     LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s' for IP `%s'.\n",
422          hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
423     if (rh->was_transmitted != GNUNET_SYSERR)
424       rh->name_callback (rh->cls, hostname);
425     rh->received_response = GNUNET_YES;
426     GNUNET_CLIENT_receive (client, &handle_response, rh,
427                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
428   }
429   /* return lookup results to caller */
430   if (NULL != rh->addr_callback)
431   {
432     struct sockaddr_in v4;
433     struct sockaddr_in6 v6;
434     const struct sockaddr *sa;
435     socklen_t salen;
436     const void *ip;
437     size_t ip_len;
438
439     ip = &msg[1];
440     ip_len = size - sizeof (struct GNUNET_MessageHeader);
441     if (ip_len == sizeof (struct in_addr))
442     {
443       memset (&v4, 0, sizeof (v4));
444       v4.sin_family = AF_INET;
445       v4.sin_addr = *(struct in_addr*) ip;
446 #if HAVE_SOCKADDR_IN_SIN_LEN
447       v4.sin_len = sizeof (v4);
448 #endif
449       salen = sizeof (v4);
450       sa = (const struct sockaddr *) &v4;
451     }
452     else if (ip_len == sizeof (struct in6_addr))
453     {
454       memset (&v6, 0, sizeof (v6));
455       v6.sin6_family = AF_INET6;
456       v6.sin6_addr = *(struct in6_addr*) ip;
457 #if HAVE_SOCKADDR_IN_SIN_LEN
458       v6.sin6_len = sizeof (v6);
459 #endif
460       salen = sizeof (v6);
461       sa = (const struct sockaddr *) &v6;
462     }
463     else
464     {
465       GNUNET_break (0);
466       if (rh->was_transmitted != GNUNET_SYSERR)
467         rh->addr_callback (rh->cls, NULL, 0);
468       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
469       GNUNET_free (rh);
470       GNUNET_CLIENT_disconnect (client);
471       client = NULL;
472       reconnect ();
473       return;
474     }
475     rh->addr_callback (rh->cls, sa, salen);
476     GNUNET_CLIENT_receive (client, &handle_response, rh,
477                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
478   }
479 }
480
481
482 /**
483  * We've been asked to lookup the address for a hostname and were
484  * given a valid numeric string.  Perform the callbacks for the
485  * numeric addresses.
486  *
487  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
488  * @param tc unused scheduler context
489  */
490 static void
491 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
492 {
493   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
494   struct sockaddr_in v4;
495   struct sockaddr_in6 v6;
496   const char *hostname;
497
498   memset (&v4, 0, sizeof (v4));
499   v4.sin_family = AF_INET;
500 #if HAVE_SOCKADDR_IN_SIN_LEN
501   v4.sin_len = sizeof (v4);
502 #endif
503   memset (&v6, 0, sizeof (v6));
504   v6.sin6_family = AF_INET6;
505 #if HAVE_SOCKADDR_IN_SIN_LEN
506   v6.sin6_len = sizeof (v6);
507 #endif
508   hostname = (const char *) &rh[1];
509   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
510       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
511   {
512     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
513     if ((rh->af == AF_UNSPEC) &&
514         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
515     {
516       /* this can happen on some systems IF "hostname" is "localhost" */
517       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
518     }
519     rh->addr_callback (rh->cls, NULL, 0);
520     GNUNET_free (rh);
521     return;
522   }
523   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
524       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
525   {
526     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
527     rh->addr_callback (rh->cls, NULL, 0);
528     GNUNET_free (rh);
529     return;
530   }
531   /* why are we here? this task should not have been scheduled! */
532   GNUNET_assert (0);
533   GNUNET_free (rh);
534 }
535
536
537 /**
538  * We've been asked to lookup the address for a hostname and were
539  * given a variant of "loopback".  Perform the callbacks for the
540  * respective loopback numeric addresses.
541  *
542  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
543  * @param tc unused scheduler context
544  */
545 static void
546 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
547 {
548   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
549   struct sockaddr_in v4;
550   struct sockaddr_in6 v6;
551
552   memset (&v4, 0, sizeof (v4));
553   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
554   v4.sin_family = AF_INET;
555 #if HAVE_SOCKADDR_IN_SIN_LEN
556   v4.sin_len = sizeof (v4);
557 #endif
558   memset (&v6, 0, sizeof (v6));
559   v6.sin6_family = AF_INET6;
560 #if HAVE_SOCKADDR_IN_SIN_LEN
561   v6.sin6_len = sizeof (v6);
562 #endif
563   v6.sin6_addr = in6addr_loopback;
564   switch (rh->af)
565   {
566   case AF_INET:
567     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
568     break;
569   case AF_INET6:
570     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
571     break;
572   case AF_UNSPEC:
573     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
574     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
575     break;
576   default:
577     GNUNET_break (0);
578     break;
579   }
580   rh->addr_callback (rh->cls, NULL, 0);
581   GNUNET_free (rh);
582 }
583
584
585 /**
586  * Task executed on system shutdown.
587  */
588 static void
589 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
590 {
591   s_task = GNUNET_SCHEDULER_NO_TASK;
592   GNUNET_RESOLVER_disconnect ();
593 }
594
595
596 /**
597  * Process pending requests to the resolver.
598  */
599 static void
600 process_requests ()
601 {
602   struct GNUNET_RESOLVER_GetMessage *msg;
603   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
604   struct GNUNET_RESOLVER_RequestHandle *rh;
605
606   if (NULL == client)
607   {
608     reconnect ();
609     return;
610   }
611   rh = req_head;
612   if (NULL == rh)
613   {
614     /* nothing to do, release socket really soon if there is nothing
615      * else happening... */
616     s_task =
617         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
618                                       &shutdown_task, NULL);
619     return;
620   }
621   if (GNUNET_YES == rh->was_transmitted)
622     return;                     /* waiting for reply */
623   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
624   msg->header.size =
625       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
626   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
627   msg->direction = htonl (rh->direction);
628   msg->af = htonl (rh->af);
629   memcpy (&msg[1], &rh[1], rh->data_len);
630   LOG (GNUNET_ERROR_TYPE_DEBUG,
631        "Transmitting DNS resolution request to DNS service\n");
632   if (GNUNET_OK !=
633       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
634                                                GNUNET_TIME_absolute_get_remaining
635                                                (rh->timeout), GNUNET_YES,
636                                                &handle_response, rh))
637   {
638     GNUNET_CLIENT_disconnect (client);
639     client = NULL;
640     reconnect ();
641     return;
642   }
643   rh->was_transmitted = GNUNET_YES;
644 }
645
646
647 /**
648  * Now try to reconnect to the resolver service.
649  *
650  * @param cls NULL
651  * @param tc scheduler context
652  */
653 static void
654 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
655 {
656   r_task = GNUNET_SCHEDULER_NO_TASK;
657   if (NULL == req_head)
658     return;                     /* no work pending */
659   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
660     return;
661   LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
662   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
663   if (NULL == client)
664   {
665     LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect, will try again later\n");
666     reconnect ();
667     return;
668   }
669   process_requests ();
670 }
671
672
673 /**
674  * Adjust exponential back-off and reconnect to the service.
675  */
676 static void
677 reconnect ()
678 {
679   struct GNUNET_RESOLVER_RequestHandle *rh;
680
681   if (GNUNET_SCHEDULER_NO_TASK != r_task)
682     return;
683   GNUNET_assert (NULL == client);
684   if (NULL != (rh = req_head))
685   {
686     switch (rh->was_transmitted)
687     {
688     case GNUNET_NO:
689       /* nothing more to do */
690       break;
691     case GNUNET_YES:
692       /* disconnected, transmit again! */
693       rh->was_transmitted = GNUNET_NO;
694       break;
695     case GNUNET_SYSERR:
696       /* request was cancelled, remove entirely */
697       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
698       GNUNET_free (rh);
699       break;
700     default:
701       GNUNET_assert (0);
702       break;
703     }
704   }
705   LOG (GNUNET_ERROR_TYPE_DEBUG,
706        "Will try to connect to DNS service in %llu ms\n",
707        (unsigned long long) backoff.rel_value);
708   GNUNET_assert (NULL != resolver_cfg);
709   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
710   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
711 }
712
713
714 /**
715  * Convert a string to one or more IP addresses.
716  *
717  * @param hostname the hostname to resolve
718  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
719  * @param callback function to call with addresses
720  * @param callback_cls closure for callback
721  * @param timeout how long to try resolving
722  * @return handle that can be used to cancel the request, NULL on error
723  */
724 struct GNUNET_RESOLVER_RequestHandle *
725 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
726                         struct GNUNET_TIME_Relative timeout,
727                         GNUNET_RESOLVER_AddressCallback callback,
728                         void *callback_cls)
729 {
730   struct GNUNET_RESOLVER_RequestHandle *rh;
731   size_t slen;
732   unsigned int i;
733   struct in_addr v4;
734   struct in6_addr v6;
735
736   slen = strlen (hostname) + 1;
737   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
738       GNUNET_SERVER_MAX_MESSAGE_SIZE)
739   {
740     GNUNET_break (0);
741     return NULL;
742   }
743   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
744   rh->af = af;
745   rh->addr_callback = callback;
746   rh->cls = callback_cls;
747   memcpy (&rh[1], hostname, slen);
748   rh->data_len = slen;
749   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
750   rh->direction = GNUNET_NO;
751   /* first, check if this is a numeric address */
752   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
753        ((af == AF_INET) || (af == AF_UNSPEC))) ||
754       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
755        ((af == AF_INET6) || (af == AF_UNSPEC))))
756   {
757     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
758     return rh;
759   }
760   /* then, check if this is a loopback address */
761   i = 0;
762   while (loopback[i] != NULL)
763     if (0 == strcasecmp (loopback[i++], hostname))
764     {
765       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
766       return rh;
767     }
768   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
769   rh->was_queued = GNUNET_YES;
770   if (s_task != GNUNET_SCHEDULER_NO_TASK)
771   {
772     GNUNET_SCHEDULER_cancel (s_task);
773     s_task = GNUNET_SCHEDULER_NO_TASK;
774   }
775   process_requests ();
776   return rh;
777 }
778
779
780 /**
781  * We've been asked to convert an address to a string without
782  * a reverse lookup.  Do it.
783  *
784  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
785  * @param tc unused scheduler context
786  */
787 static void
788 numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
789 {
790   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
791   char *result;
792
793   result = no_resolve (rh->af, &rh[1], rh->data_len);
794   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s'.\n", result);
795   if (result != NULL)
796   {
797     rh->name_callback (rh->cls, result);
798     GNUNET_free (result);
799   }
800   rh->name_callback (rh->cls, NULL);
801   GNUNET_free (rh);
802 }
803
804
805 /**
806  * Get an IP address as a string.
807  *
808  * @param sa host address
809  * @param salen length of host address
810  * @param do_resolve use GNUNET_NO to return numeric hostname
811  * @param timeout how long to try resolving
812  * @param callback function to call with hostnames
813  *        last callback is NULL when finished
814  * @param cls closure for callback
815  * @return handle that can be used to cancel the request
816  */
817 struct GNUNET_RESOLVER_RequestHandle *
818 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
819                               int do_resolve,
820                               struct GNUNET_TIME_Relative timeout,
821                               GNUNET_RESOLVER_HostnameCallback callback,
822                               void *cls)
823 {
824   struct GNUNET_RESOLVER_RequestHandle *rh;
825   size_t ip_len;
826   const void *ip;
827
828   check_config ();
829   switch (sa->sa_family)
830   {
831   case AF_INET:
832     ip_len = sizeof (struct in_addr);
833     ip = &((const struct sockaddr_in*)sa)->sin_addr;
834     break;
835   case AF_INET6:
836     ip_len = sizeof (struct in6_addr);
837     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
838     break;
839   default:
840     GNUNET_break (0);
841     return NULL;
842   }
843   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
844   rh->name_callback = callback;
845   rh->cls = cls;
846   rh->af = sa->sa_family;
847   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
848   memcpy (&rh[1], ip, ip_len);
849   rh->data_len = ip_len;
850   rh->direction = GNUNET_YES;
851   rh->received_response = GNUNET_NO;
852   if (GNUNET_NO == do_resolve)
853   {
854     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
855     return rh;
856   }
857   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
858   rh->was_queued = GNUNET_YES;
859   if (s_task != GNUNET_SCHEDULER_NO_TASK)
860   {
861     GNUNET_SCHEDULER_cancel (s_task);
862     s_task = GNUNET_SCHEDULER_NO_TASK;
863   }
864   process_requests ();
865   return rh;
866 }
867
868
869 /**
870  * Get local fully qualified af name
871  *
872  * @return fqdn
873  */
874 char *
875 GNUNET_RESOLVER_local_fqdn_get ()
876 {
877   struct hostent *host;
878   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
879
880   if (0 != gethostname (hostname, sizeof (hostname) - 1))
881   {
882     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
883                   "gethostname");
884     return NULL;
885   }
886   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our FQDN `%s'\n", hostname);
887   host = gethostbyname (hostname);
888   if (NULL == host)
889   {
890     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
891          hstrerror (h_errno));
892     return NULL;
893   }
894   return GNUNET_strdup (host->h_name);
895 }
896
897
898 /**
899  * Looking our own hostname.
900  *
901  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
902  * @param callback function to call with addresses
903  * @param cls closure for callback
904  * @param timeout how long to try resolving
905  * @return handle that can be used to cancel the request, NULL on error
906  */
907 struct GNUNET_RESOLVER_RequestHandle *
908 GNUNET_RESOLVER_hostname_resolve (int af,
909                                   struct GNUNET_TIME_Relative timeout,
910                                   GNUNET_RESOLVER_AddressCallback callback,
911                                   void *cls)
912 {
913   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
914
915   if (0 != gethostname (hostname, sizeof (hostname) - 1))
916   {
917     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
918                   "gethostname");
919     return NULL;
920   }
921   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our hostname `%s'\n", hostname);
922   return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
923 }
924
925
926 /**
927  * Cancel a request that is still pending with the resolver.
928  * Note that a client MUST NOT cancel a request that has
929  * been completed (i.e, the callback has been called to
930  * signal timeout or the final result).
931  *
932  * @param rh handle of request to cancel
933  */
934 void
935 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
936 {
937   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
938   {
939     GNUNET_SCHEDULER_cancel (rh->task);
940     rh->task = GNUNET_SCHEDULER_NO_TASK;
941   }
942   if (rh->was_transmitted == GNUNET_NO)
943   {
944     if (rh->was_queued == GNUNET_YES)
945       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
946     GNUNET_free (rh);
947     return;
948   }
949   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
950   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
951 }
952
953
954 /* end of resolver_api.c */