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