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