the big NAT change
[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       if (NULL != rh->name_callback)
352         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353                     _("Timeout trying to resolve IP address `%s'.\n"),
354                     GNUNET_a2s ((const void*) &rh[1], rh->data_len));
355       else
356         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357                     _("Timeout trying to resolve hostname `%s'.\n"),
358                     (const char *) &rh[1]);
359       if (rh->was_transmitted != GNUNET_SYSERR)
360         {
361           if (NULL != rh->name_callback)
362             rh->name_callback (rh->cls, NULL);      
363           if (NULL != rh->addr_callback)
364             rh->addr_callback (rh->cls, NULL, 0);
365         }
366       GNUNET_CONTAINER_DLL_remove (req_head,
367                                    req_tail,
368                                    rh);
369       GNUNET_free (rh);
370       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
371       client = NULL;
372       reconnect ();
373       return;
374     }
375   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
376     {
377       GNUNET_break (0);
378       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
379       client = NULL;
380       reconnect ();
381       return;
382     }
383   size = ntohs (msg->size);
384   if (size == sizeof (struct GNUNET_MessageHeader))
385     {
386       if (rh->was_transmitted != GNUNET_SYSERR)
387         {
388           if (NULL != rh->name_callback)
389             rh->name_callback (rh->cls, NULL);      
390           if (NULL != rh->addr_callback)
391             rh->addr_callback (rh->cls, NULL, 0);
392         }
393       GNUNET_CONTAINER_DLL_remove (req_head,
394                                    req_tail,
395                                    rh);
396       GNUNET_free (rh);
397       process_requests ();
398       return;
399     }
400   if (NULL != rh->name_callback)
401     {
402       hostname = (const char *) &msg[1];
403       if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
404         {
405           GNUNET_break (0);
406           if (rh->was_transmitted != GNUNET_SYSERR)
407             rh->name_callback (rh->cls, NULL);
408           GNUNET_CONTAINER_DLL_remove (req_head,
409                                        req_tail,
410                                        rh);       
411           GNUNET_free (rh);
412           GNUNET_CLIENT_disconnect (client, GNUNET_NO);
413           client = NULL;
414           reconnect ();
415           return;
416         }
417 #if DEBUG_RESOLVER
418       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419                   _("Resolver returns `%s' for IP `%s'.\n"), 
420                   hostname,
421                   GNUNET_a2s ((const void*) &rh[1], rh->data_len));
422 #endif
423       if (rh->was_transmitted != GNUNET_SYSERR)
424         rh->name_callback (rh->cls, hostname);
425       GNUNET_CLIENT_receive (client,
426                              &handle_response,
427                              rh,
428                              GNUNET_TIME_absolute_get_remaining (rh->timeout));
429     }
430   if (NULL != rh->addr_callback)
431     {
432       sa = (const struct sockaddr *) &msg[1];
433       salen = size - sizeof (struct GNUNET_MessageHeader);
434       if (salen < sizeof (struct sockaddr))
435         {
436           GNUNET_break (0);
437           if (rh->was_transmitted != GNUNET_SYSERR)
438             rh->addr_callback (rh->cls, NULL, 0);
439           GNUNET_CONTAINER_DLL_remove (req_head,
440                                        req_tail,
441                                        rh);       
442           GNUNET_free (rh);
443           GNUNET_CLIENT_disconnect (client, GNUNET_NO);
444           client = NULL;
445           reconnect ();
446           return;
447         }
448 #if DEBUG_RESOLVER
449       {
450         char *ips = no_resolve (sa, salen);
451         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452                     "Resolver returns `%s' for `%s'.\n", 
453                     ips,
454                     (const char*) &rh[1]);
455         GNUNET_free (ips);
456       }
457 #endif
458       rh->addr_callback (rh->cls, sa, salen);
459       GNUNET_CLIENT_receive (client,
460                              &handle_response,
461                              rh,
462                              GNUNET_TIME_absolute_get_remaining (rh->timeout));
463     }
464 }
465
466
467 /**
468  * We've been asked to lookup the address for a hostname and were 
469  * given a valid numeric string.  Perform the callbacks for the
470  * numeric addresses.
471  *
472  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
473  * @param tc unused scheduler context
474  */
475 static void
476 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
477 {
478   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
479   struct sockaddr_in v4;
480   struct sockaddr_in6 v6;
481   const char *hostname;
482
483   memset (&v4, 0, sizeof (v4));
484   v4.sin_family = AF_INET;
485 #if HAVE_SOCKADDR_IN_SIN_LEN
486   v4.sin_len = sizeof (v4);
487 #endif
488   memset (&v6, 0, sizeof (v6));
489   v6.sin6_family = AF_INET6;
490 #if HAVE_SOCKADDR_IN_SIN_LEN
491   v6.sin6_len = sizeof (v6);
492 #endif
493   hostname = (const char*) &rh[1];
494   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET)) &&
495       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
496     {
497       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
498       if ((rh->domain == AF_UNSPEC) &&
499           (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
500         {
501           /* this can happen on some systems IF "hostname" is "localhost" */
502           rh->addr_callback (rh->cls,
503                              (const struct sockaddr *) &v6, sizeof (v6));
504         }
505       rh->addr_callback (rh->cls, NULL, 0);
506       GNUNET_free (rh);
507       return;
508     }
509   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6)) &&
510       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
511     {
512       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
513       rh->addr_callback (rh->cls, NULL, 0);
514       GNUNET_free (rh);
515       return;
516     }
517   /* why are we here? this task should not have been scheduled! */
518   GNUNET_assert (0);
519   GNUNET_free (rh);
520 }
521
522
523 /**
524  * We've been asked to lookup the address for a hostname and were 
525  * given a variant of "loopback".  Perform the callbacks for the
526  * respective loopback numeric addresses.
527  *
528  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
529  * @param tc unused scheduler context
530  */
531 static void
532 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
533 {
534   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
535   struct sockaddr_in v4;
536   struct sockaddr_in6 v6;
537
538   memset (&v4, 0, sizeof (v4));
539   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
540   v4.sin_family = AF_INET;
541 #if HAVE_SOCKADDR_IN_SIN_LEN
542   v4.sin_len = sizeof (v4);
543 #endif
544   memset (&v6, 0, sizeof (v6));
545   v6.sin6_family = AF_INET6;
546 #if HAVE_SOCKADDR_IN_SIN_LEN
547   v6.sin6_len = sizeof (v6);
548 #endif
549   v6.sin6_addr = in6addr_loopback;
550   switch (rh->domain)
551     {
552     case AF_INET:
553       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
554       break;
555     case AF_INET6:
556       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
557       break;
558     case AF_UNSPEC:
559       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
560       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
561       break;
562     default:
563       GNUNET_break (0);
564       break;
565     }
566   rh->addr_callback (rh->cls, NULL, 0);
567   GNUNET_free (rh);
568 }
569
570
571 /**
572  * Task executed on system shutdown.
573  */
574 static void
575 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
576 {
577   s_task = GNUNET_SCHEDULER_NO_TASK;
578   GNUNET_RESOLVER_disconnect ();
579 }
580
581
582 /**
583  * Process pending requests to the resolver.
584  *
585  * @param h handle to the resolver
586  */
587 static void
588 process_requests ()
589 {
590   struct GNUNET_RESOLVER_GetMessage *msg;
591   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
592   struct GNUNET_RESOLVER_RequestHandle *rh;
593   
594   if (NULL == client)
595     {
596       reconnect ();
597       return;
598     }
599   rh = req_head;
600   if (NULL == rh)
601     {
602       /* nothing to do, release socket really soon if there is nothing
603          else happening... */
604       s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
605                                              &shutdown_task, NULL);
606       return; 
607     }
608   if (GNUNET_YES == rh->was_transmitted)
609     return; /* waiting for reply */
610   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
611   msg->header.size =
612     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
613   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
614   msg->direction = htonl (rh->direction);
615   msg->domain = htonl (rh->domain);
616   memcpy (&msg[1], &rh[1], rh->data_len);
617 #if DEBUG_RESOLVER
618   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619               "Transmitting DNS resolution request to DNS service\n");
620 #endif
621   if (GNUNET_OK !=
622       GNUNET_CLIENT_transmit_and_get_response (client,
623                                                &msg->header,
624                                                GNUNET_TIME_absolute_get_remaining (rh->timeout),
625                                                GNUNET_YES,
626                                                &handle_response, rh))
627     {
628       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
629       client = NULL;
630       reconnect ();
631       return;
632     }
633   rh->was_transmitted = GNUNET_YES;
634 }
635
636
637 /**
638  * Now try to reconnect to the resolver service.
639  *
640  * @param cls NULL
641  * @param tc scheduler context
642  */
643 static void
644 reconnect_task (void *cls,
645                 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,
654               "Trying to connect to DNS service\n");
655 #endif
656   client = GNUNET_CLIENT_connect ("resolver", cfg);
657   if (NULL == client)
658     {
659       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660                   "Failed to connect, will try again later\n");
661       reconnect ();
662       return;
663     }
664   process_requests ();
665 }
666
667
668 /**
669  * Adjust exponential back-off and reconnect to the service.
670  */
671 static void
672 reconnect ()
673 {
674   struct GNUNET_RESOLVER_RequestHandle *rh;
675
676   if (GNUNET_SCHEDULER_NO_TASK != r_task)
677     return;
678   GNUNET_assert (NULL == client);
679   if (NULL != (rh = req_head))
680     {
681       switch (rh->was_transmitted)
682         {
683         case GNUNET_NO:
684           /* nothing more to do */
685           break;
686         case GNUNET_YES:
687           /* disconnected, transmit again! */
688           rh->was_transmitted = GNUNET_NO;
689           break;
690         case GNUNET_SYSERR:
691           /* request was cancelled, remove entirely */
692           GNUNET_CONTAINER_DLL_remove (req_head,
693                                        req_tail,
694                                        rh);
695           GNUNET_free (rh);
696           break;
697         default:
698           GNUNET_assert (0);
699           break;
700         }
701     }
702 #if DEBUG_RESOLVER
703   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
704               "Will try to connect to DNS service in %llu ms\n",
705               (unsigned long long) backoff.rel_value);
706 #endif
707   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
708                                          &reconnect_task,
709                                          NULL);
710   backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_SECONDS,
711                                       GNUNET_TIME_relative_multiply (backoff, 2));
712 }
713
714
715 /**
716  * Convert a string to one or more IP addresses.
717  *
718  * @param hostname the hostname to resolve
719  * @param domain 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,
727                         int domain,
728                         struct GNUNET_TIME_Relative timeout,
729                         GNUNET_RESOLVER_AddressCallback callback,
730                         void *callback_cls)
731 {
732   struct GNUNET_RESOLVER_RequestHandle *rh;
733   size_t slen;
734   unsigned int i;
735   struct in_addr v4;
736   struct in6_addr v6;
737
738   slen = strlen (hostname) + 1;
739   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >= 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->domain = domain;
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,
754                         hostname,
755                         &v4)) &&
756        ((domain == AF_INET) || (domain == AF_UNSPEC))) ||
757       ((1 == inet_pton (AF_INET6,
758                         hostname,
759                         &v6)) &&
760        ((domain == AF_INET6) || (domain == AF_UNSPEC))))
761     {
762       rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
763       return rh;
764     }
765   /* then, check if this is a loopback address */
766   i = 0;
767   while (loopback[i] != NULL)
768     if (0 == strcasecmp (loopback[i++], hostname))
769       {
770         rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
771         return rh;
772       }
773   GNUNET_CONTAINER_DLL_insert_tail (req_head,
774                                     req_tail,
775                                     rh);
776   rh->was_queued = GNUNET_YES;
777   if (s_task != GNUNET_SCHEDULER_NO_TASK)
778     {
779       GNUNET_SCHEDULER_cancel (s_task);
780       s_task = GNUNET_SCHEDULER_NO_TASK;
781     }
782   process_requests ();
783   return rh;
784 }
785
786
787 /**
788  * We've been asked to convert an address to a string without
789  * a reverse lookup.  Do it.
790  *
791  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
792  * @param tc unused scheduler context
793  */
794 static void
795 numeric_reverse (void *cls, 
796                  const struct GNUNET_SCHEDULER_TaskContext *tc)
797 {
798   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
799   char *result;
800
801   result = no_resolve ((const struct sockaddr *) &rh[1], rh->data_len);
802 #if DEBUG_RESOLVER
803   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
804 #endif
805   if (result != NULL)
806     {
807       rh->name_callback (rh->cls, result);
808       GNUNET_free (result);
809     }
810   rh->name_callback (rh->cls, NULL);
811   GNUNET_free (rh);
812 }
813
814
815 /**
816  * Get an IP address as a string.
817  *
818  * @param sa host address
819  * @param salen length of host address
820  * @param do_resolve use GNUNET_NO to return numeric hostname
821  * @param timeout how long to try resolving
822  * @param callback function to call with hostnames
823  * @param cls closure for callback
824  * @return handle that can be used to cancel the request
825  */
826 struct GNUNET_RESOLVER_RequestHandle *
827 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
828                               socklen_t salen,
829                               int do_resolve,
830                               struct GNUNET_TIME_Relative timeout,
831                               GNUNET_RESOLVER_HostnameCallback callback,
832                               void *cls)
833 {
834   struct GNUNET_RESOLVER_RequestHandle *rh;
835
836   check_config (cfg);
837   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
838   rh->name_callback = callback;
839   rh->cls = cls;
840   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
841   memcpy (&rh[1], sa, salen);
842   rh->data_len = salen;
843   rh->direction = GNUNET_YES;
844   if (GNUNET_NO == do_resolve)
845     {
846       rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
847       return rh;
848     }
849   if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
850     {
851       GNUNET_break (0);
852       GNUNET_free (rh);
853       return NULL;
854     }
855   GNUNET_CONTAINER_DLL_insert_tail (req_head,
856                                     req_tail,
857                                     rh);
858   rh->was_queued = GNUNET_YES;
859   if (s_task != GNUNET_SCHEDULER_NO_TASK)
860     {
861       GNUNET_SCHEDULER_cancel (s_task);
862       s_task = GNUNET_SCHEDULER_NO_TASK;
863     }
864   process_requests ();
865   return rh;
866 }
867
868
869 /**
870  * Get local fully qualified domain name
871  *
872  * @return fqdn
873  */
874 char *
875 GNUNET_RESOLVER_local_fqdn_get ()
876 {
877   struct hostent *host;
878   char hostname[GNUNET_OS_get_hostname_max_length() + 1];
879
880   if (0 != gethostname (hostname, sizeof (hostname) - 1))
881     {
882       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
883                            GNUNET_ERROR_TYPE_BULK, "gethostname");
884       return NULL;
885     }
886 #if DEBUG_RESOLVER
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888               _("Resolving our FQDN `%s'\n"), hostname);
889 #endif
890   host = gethostbyname (hostname);
891   if (NULL == host)
892     {
893       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
894                   _("Could not resolve our FQDN : %s\n"),
895                   hstrerror (h_errno));
896       return NULL;
897     }
898   return GNUNET_strdup (host->h_name);
899 }
900
901
902 /**
903  * Looking our own hostname.
904  *
905  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
906  * @param callback function to call with addresses
907  * @param cls closure for callback
908  * @param timeout how long to try resolving
909  * @return handle that can be used to cancel the request, NULL on error
910  */
911 struct GNUNET_RESOLVER_RequestHandle *
912 GNUNET_RESOLVER_hostname_resolve (int domain,
913                                   struct GNUNET_TIME_Relative timeout,
914                                   GNUNET_RESOLVER_AddressCallback callback,
915                                   void *cls)
916 {
917   char hostname[GNUNET_OS_get_hostname_max_length() + 1];
918
919   if (0 != gethostname (hostname, sizeof (hostname) - 1))
920     {
921       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
922                            GNUNET_ERROR_TYPE_BULK, "gethostname");
923       return NULL;
924     }
925 #if DEBUG_RESOLVER
926   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
927               _("Resolving our hostname `%s'\n"), hostname);
928 #endif
929   return GNUNET_RESOLVER_ip_get (hostname,
930                                  domain, 
931                                  timeout,
932                                  callback, cls);
933 }
934
935
936 /**
937  * Cancel a request that is still pending with the resolver.
938  * Note that a client MUST NOT cancel a request that has
939  * been completed (i.e, the callback has been called to
940  * signal timeout or the final result).
941  *
942  * @param rh handle of request to cancel
943  */
944 void
945 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
946 {
947   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
948     {
949       GNUNET_SCHEDULER_cancel (rh->task);
950       rh->task = GNUNET_SCHEDULER_NO_TASK;
951     }
952   if (rh->was_transmitted == GNUNET_NO)
953     {
954       if (rh->was_queued == GNUNET_YES)
955         GNUNET_CONTAINER_DLL_remove (req_head,
956                                      req_tail,
957                                      rh);
958       GNUNET_free (rh);
959       return;
960     }
961   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
962   rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */
963 }
964
965
966 /* end of resolver_api.c */