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