doxygen
[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 /**
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 domain;
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     GNUNET_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 (loopback[i] != NULL)
210     if (0 == strcasecmp (loopback[i++], hostname))
211     {
212       GNUNET_free (hostname);
213       return;
214     }
215   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216               _
217               ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
218               "localhost", "HOSTNAME", "resolver");
219   GNUNET_free (hostname);
220   GNUNET_assert (0);
221 }
222
223
224 /**
225  * Create the connection to the resolver service.
226  *
227  * @param cfg configuration to use
228  */
229 void
230 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
231 {
232   GNUNET_assert (NULL != cfg);
233   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
234   resolver_cfg = cfg;
235   check_config ();
236 }
237
238
239 /**
240  * Destroy the connection to the resolver service.
241  */
242 void
243 GNUNET_RESOLVER_disconnect ()
244 {
245   GNUNET_assert (NULL == req_head);
246   GNUNET_assert (NULL == req_tail);
247   if (NULL != client)
248   {
249 #if DEBUG_RESOLVER
250     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
251 #endif
252     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
253     client = NULL;
254   }
255   if (r_task != GNUNET_SCHEDULER_NO_TASK)
256   {
257     GNUNET_SCHEDULER_cancel (r_task);
258     r_task = GNUNET_SCHEDULER_NO_TASK;
259   }
260   if (s_task != GNUNET_SCHEDULER_NO_TASK)
261   {
262     GNUNET_SCHEDULER_cancel (s_task);
263     s_task = GNUNET_SCHEDULER_NO_TASK;
264   }
265 }
266
267
268 /**
269  * Convert IP address to string without DNS resolution.
270  *
271  * @param sa the address
272  * @param salen number of bytes in sa
273  * @return address as a string, NULL on error
274  */
275 static char *
276 no_resolve (const struct sockaddr *sa, socklen_t salen)
277 {
278   char *ret;
279   char inet4[INET_ADDRSTRLEN];
280   char inet6[INET6_ADDRSTRLEN];
281
282   if (salen < sizeof (struct sockaddr))
283     return NULL;
284   switch (sa->sa_family)
285   {
286   case AF_INET:
287     if (salen != sizeof (struct sockaddr_in))
288       return NULL;
289     if (NULL ==
290         inet_ntop (AF_INET, &((struct sockaddr_in *) sa)->sin_addr, inet4,
291                    INET_ADDRSTRLEN))
292     {
293       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
294       return NULL;
295     }
296     ret = GNUNET_strdup (inet4);
297     break;
298   case AF_INET6:
299     if (salen != sizeof (struct sockaddr_in6))
300       return NULL;
301     if (NULL ==
302         inet_ntop (AF_INET6, &((struct sockaddr_in6 *) sa)->sin6_addr, inet6,
303                    INET6_ADDRSTRLEN))
304     {
305       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
306       return NULL;
307     }
308     ret = GNUNET_strdup (inet6);
309     break;
310   default:
311     ret = NULL;
312     break;
313   }
314   return ret;
315 }
316
317
318 /**
319  * Adjust exponential back-off and reconnect to the service.
320  */
321 static void
322 reconnect ();
323
324
325 /**
326  * Process pending requests to the resolver.
327  */
328 static void
329 process_requests ();
330
331
332 /**
333  * Process response with a hostname for a DNS lookup.
334  *
335  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
336  * @param msg message with the hostname, NULL on error
337  */
338 static void
339 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
340 {
341   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
342   uint16_t size;
343   const char *hostname;
344   const struct sockaddr *sa;
345   socklen_t salen;
346
347 #if DEBUG_RESOLVER
348   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
349 #endif
350   if (msg == NULL)
351   {
352     if (NULL != rh->name_callback)
353       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
354                   _("Timeout trying to resolve IP address `%s'.\n"),
355                   GNUNET_a2s ((const void *) &rh[1], rh->data_len));
356     else
357       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
358                   _("Timeout trying to resolve hostname `%s'.\n"),
359                   (const char *) &rh[1]);
360     /* check if request was canceled */
361     if (rh->was_transmitted != GNUNET_SYSERR)
362     {
363       if (NULL != rh->name_callback)
364       {
365         /* no reverse lookup was successful, return ip as string */
366         if (rh->received_response == GNUNET_NO)
367           rh->name_callback (rh->cls,
368                              no_resolve ((const struct sockaddr *) &rh[1],
369                                          rh->data_len));
370         /* at least one reverse lookup was successful */
371         else
372           rh->name_callback (rh->cls, NULL);
373       }
374       if (NULL != rh->addr_callback)
375         rh->addr_callback (rh->cls, NULL, 0);
376     }
377     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
378     GNUNET_free (rh);
379     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
380     client = NULL;
381     reconnect ();
382     return;
383   }
384   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
385   {
386     GNUNET_break (0);
387     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
388     client = NULL;
389     reconnect ();
390     return;
391   }
392   size = ntohs (msg->size);
393   /* message contains not data, just header */
394   if (size == sizeof (struct GNUNET_MessageHeader))
395   {
396     /* check if request was canceled */
397     if (rh->was_transmitted != GNUNET_SYSERR)
398     {
399       if (NULL != rh->name_callback)
400         rh->name_callback (rh->cls, NULL);
401       if (NULL != rh->addr_callback)
402         rh->addr_callback (rh->cls, NULL, 0);
403     }
404     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
405     GNUNET_free (rh);
406     process_requests ();
407     return;
408   }
409   /* return reverse lookup results to caller */
410   if (NULL != rh->name_callback)
411   {
412     hostname = (const char *) &msg[1];
413     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
414     {
415       GNUNET_break (0);
416       if (rh->was_transmitted != GNUNET_SYSERR)
417         rh->name_callback (rh->cls, NULL);
418       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
419       GNUNET_free (rh);
420       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
421       client = NULL;
422       reconnect ();
423       return;
424     }
425 #if DEBUG_RESOLVER
426     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427                 _("Resolver returns `%s' for IP `%s'.\n"), hostname,
428                 GNUNET_a2s ((const void *) &rh[1], rh->data_len));
429 #endif
430     if (rh->was_transmitted != GNUNET_SYSERR)
431       rh->name_callback (rh->cls, hostname);
432     rh->received_response = GNUNET_YES;
433     GNUNET_CLIENT_receive (client, &handle_response, rh,
434                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
435   }
436   /* return lookup results to caller */
437   if (NULL != rh->addr_callback)
438   {
439     sa = (const struct sockaddr *) &msg[1];
440     salen = size - sizeof (struct GNUNET_MessageHeader);
441     if (salen < sizeof (struct sockaddr))
442     {
443       GNUNET_break (0);
444       if (rh->was_transmitted != GNUNET_SYSERR)
445         rh->addr_callback (rh->cls, NULL, 0);
446       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
447       GNUNET_free (rh);
448       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
449       client = NULL;
450       reconnect ();
451       return;
452     }
453 #if DEBUG_RESOLVER
454     {
455       char *ips = no_resolve (sa, salen);
456
457       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s' for `%s'.\n",
458                   ips, (const char *) &rh[1]);
459       GNUNET_free (ips);
460     }
461 #endif
462     rh->addr_callback (rh->cls, sa, salen);
463     GNUNET_CLIENT_receive (client, &handle_response, rh,
464                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
465   }
466 }
467
468
469 /**
470  * We've been asked to lookup the address for a hostname and were
471  * given a valid numeric string.  Perform the callbacks for the
472  * numeric addresses.
473  *
474  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
475  * @param tc unused scheduler context
476  */
477 static void
478 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
479 {
480   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
481   struct sockaddr_in v4;
482   struct sockaddr_in6 v6;
483   const char *hostname;
484
485   memset (&v4, 0, sizeof (v4));
486   v4.sin_family = AF_INET;
487 #if HAVE_SOCKADDR_IN_SIN_LEN
488   v4.sin_len = sizeof (v4);
489 #endif
490   memset (&v6, 0, sizeof (v6));
491   v6.sin6_family = AF_INET6;
492 #if HAVE_SOCKADDR_IN_SIN_LEN
493   v6.sin6_len = sizeof (v6);
494 #endif
495   hostname = (const char *) &rh[1];
496   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET)) &&
497       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
498   {
499     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
500     if ((rh->domain == AF_UNSPEC) &&
501         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
502     {
503       /* this can happen on some systems IF "hostname" is "localhost" */
504       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
505     }
506     rh->addr_callback (rh->cls, NULL, 0);
507     GNUNET_free (rh);
508     return;
509   }
510   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6)) &&
511       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
512   {
513     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
514     rh->addr_callback (rh->cls, NULL, 0);
515     GNUNET_free (rh);
516     return;
517   }
518   /* why are we here? this task should not have been scheduled! */
519   GNUNET_assert (0);
520   GNUNET_free (rh);
521 }
522
523
524 /**
525  * We've been asked to lookup the address for a hostname and were
526  * given a variant of "loopback".  Perform the callbacks for the
527  * respective loopback numeric addresses.
528  *
529  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
530  * @param tc unused scheduler context
531  */
532 static void
533 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
534 {
535   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
536   struct sockaddr_in v4;
537   struct sockaddr_in6 v6;
538
539   memset (&v4, 0, sizeof (v4));
540   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
541   v4.sin_family = AF_INET;
542 #if HAVE_SOCKADDR_IN_SIN_LEN
543   v4.sin_len = sizeof (v4);
544 #endif
545   memset (&v6, 0, sizeof (v6));
546   v6.sin6_family = AF_INET6;
547 #if HAVE_SOCKADDR_IN_SIN_LEN
548   v6.sin6_len = sizeof (v6);
549 #endif
550   v6.sin6_addr = in6addr_loopback;
551   switch (rh->domain)
552   {
553   case AF_INET:
554     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
555     break;
556   case AF_INET6:
557     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
558     break;
559   case AF_UNSPEC:
560     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
561     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
562     break;
563   default:
564     GNUNET_break (0);
565     break;
566   }
567   rh->addr_callback (rh->cls, NULL, 0);
568   GNUNET_free (rh);
569 }
570
571
572 /**
573  * Task executed on system shutdown.
574  */
575 static void
576 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
577 {
578   s_task = GNUNET_SCHEDULER_NO_TASK;
579   GNUNET_RESOLVER_disconnect ();
580 }
581
582
583 /**
584  * Process pending requests to the resolver.
585  *
586  * @param h handle to the resolver
587  */
588 static void
589 process_requests ()
590 {
591   struct GNUNET_RESOLVER_GetMessage *msg;
592   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
593   struct GNUNET_RESOLVER_RequestHandle *rh;
594
595   if (NULL == client)
596   {
597     reconnect ();
598     return;
599   }
600   rh = req_head;
601   if (NULL == rh)
602   {
603     /* nothing to do, release socket really soon if there is nothing
604      * else happening... */
605     s_task =
606         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
607                                       &shutdown_task, NULL);
608     return;
609   }
610   if (GNUNET_YES == rh->was_transmitted)
611     return;                     /* waiting for reply */
612   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
613   msg->header.size =
614       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
615   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
616   msg->direction = htonl (rh->direction);
617   msg->domain = htonl (rh->domain);
618   memcpy (&msg[1], &rh[1], rh->data_len);
619 #if DEBUG_RESOLVER
620   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621               "Transmitting DNS resolution request to DNS service\n");
622 #endif
623   if (GNUNET_OK !=
624       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
625                                                GNUNET_TIME_absolute_get_remaining
626                                                (rh->timeout), GNUNET_YES,
627                                                &handle_response, rh))
628   {
629     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
630     client = NULL;
631     reconnect ();
632     return;
633   }
634   rh->was_transmitted = GNUNET_YES;
635 }
636
637
638 /**
639  * Now try to reconnect to the resolver service.
640  *
641  * @param cls NULL
642  * @param tc scheduler context
643  */
644 static void
645 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
646 {
647   r_task = GNUNET_SCHEDULER_NO_TASK;
648   if (NULL == req_head)
649     return;                     /* no work pending */
650   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
651     return;
652 #if DEBUG_RESOLVER
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
654 #endif
655   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
656   if (NULL == client)
657   {
658     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659                 "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   GNUNET_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   GNUNET_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_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
871                          "gethostname");
872     return NULL;
873   }
874 #if DEBUG_RESOLVER
875   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our FQDN `%s'\n"),
876               hostname);
877 #endif
878   host = gethostbyname (hostname);
879   if (NULL == host)
880   {
881     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
882                 hstrerror (h_errno));
883     return NULL;
884   }
885   return GNUNET_strdup (host->h_name);
886 }
887
888
889 /**
890  * Looking our own hostname.
891  *
892  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
893  * @param callback function to call with addresses
894  * @param cls closure for callback
895  * @param timeout how long to try resolving
896  * @return handle that can be used to cancel the request, NULL on error
897  */
898 struct GNUNET_RESOLVER_RequestHandle *
899 GNUNET_RESOLVER_hostname_resolve (int domain,
900                                   struct GNUNET_TIME_Relative timeout,
901                                   GNUNET_RESOLVER_AddressCallback callback,
902                                   void *cls)
903 {
904   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
905
906   if (0 != gethostname (hostname, sizeof (hostname) - 1))
907   {
908     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
909                          "gethostname");
910     return NULL;
911   }
912 #if DEBUG_RESOLVER
913   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our hostname `%s'\n"),
914               hostname);
915 #endif
916   return GNUNET_RESOLVER_ip_get (hostname, domain, timeout, callback, cls);
917 }
918
919
920 /**
921  * Cancel a request that is still pending with the resolver.
922  * Note that a client MUST NOT cancel a request that has
923  * been completed (i.e, the callback has been called to
924  * signal timeout or the final result).
925  *
926  * @param rh handle of request to cancel
927  */
928 void
929 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
930 {
931   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
932   {
933     GNUNET_SCHEDULER_cancel (rh->task);
934     rh->task = GNUNET_SCHEDULER_NO_TASK;
935   }
936   if (rh->was_transmitted == GNUNET_NO)
937   {
938     if (rh->was_queued == GNUNET_YES)
939       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
940     GNUNET_free (rh);
941     return;
942   }
943   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
944   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
945 }
946
947
948 /* end of resolver_api.c */