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