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