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