9a2228da0591fc39a87d217836334efcf7a23b8c
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-2013 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 3, 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_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_resolver_service.h"
30 #include "resolver.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
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 *resolver_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 af;
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 ()
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 (resolver_cfg, "resolver",
195                                              "HOSTNAME", &hostname))
196   {
197     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   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 cfg configuration to use
228  */
229 void
230 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
231 {
232   GNUNET_assert (NULL != cfg);
233   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
234   resolver_cfg = cfg;
235   check_config ();
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     LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
250     GNUNET_CLIENT_disconnect (client);
251     client = NULL;
252   }
253   if (r_task != GNUNET_SCHEDULER_NO_TASK)
254   {
255     GNUNET_SCHEDULER_cancel (r_task);
256     r_task = GNUNET_SCHEDULER_NO_TASK;
257   }
258   if (s_task != GNUNET_SCHEDULER_NO_TASK)
259   {
260     GNUNET_SCHEDULER_cancel (s_task);
261     s_task = GNUNET_SCHEDULER_NO_TASK;
262   }
263 }
264
265
266 /**
267  * Convert IP address to string without DNS resolution.
268  *
269  * @param af address family
270  * @param ip the address
271  * @param ip_len number of bytes in ip
272  * @return address as a string, NULL on error
273  */
274 static char *
275 no_resolve (int af,
276             const void *ip, socklen_t ip_len)
277 {
278   char buf[INET6_ADDRSTRLEN];
279
280   switch (af)
281   {
282   case AF_INET:
283     if (ip_len != sizeof (struct in_addr))
284       return NULL;
285     if (NULL ==
286         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
287     {
288       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
289       return NULL;
290     }
291     break;
292   case AF_INET6:
293     if (ip_len != sizeof (struct in6_addr))
294       return NULL;
295     if (NULL ==
296         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
297     {
298       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
299       return NULL;
300     }
301     break;
302   default:
303     GNUNET_break (0);
304     return NULL;
305   }
306   return GNUNET_strdup (buf);
307 }
308
309
310 /**
311  * Adjust exponential back-off and reconnect to the service.
312  */
313 static void
314 reconnect ();
315
316
317 /**
318  * Process pending requests to the resolver.
319  */
320 static void
321 process_requests ();
322
323
324 /**
325  * Process response with a hostname for a DNS lookup.
326  *
327  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
328  * @param msg message with the hostname, NULL on error
329  */
330 static void
331 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
332 {
333   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
334   uint16_t size;
335
336   LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
337   if (msg == NULL)
338   {
339     char buf[INET6_ADDRSTRLEN];
340
341     if (NULL != rh->name_callback)
342       LOG (GNUNET_ERROR_TYPE_INFO,
343            _("Timeout trying to resolve IP address `%s'.\n"),
344            inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
345     else
346       LOG (GNUNET_ERROR_TYPE_INFO,
347            _("Timeout trying to resolve hostname `%s'.\n"),
348            (const char *) &rh[1]);
349     /* check if request was canceled */
350     if (rh->was_transmitted != GNUNET_SYSERR)
351     {
352       if (NULL != rh->name_callback)
353       {
354         /* no reverse lookup was successful, return ip as string */
355         if (rh->received_response == GNUNET_NO)
356         {
357           rh->name_callback (rh->cls, no_resolve (rh->af, &rh[1], rh->data_len));
358           rh->name_callback (rh->cls, NULL);
359         }
360         /* at least one reverse lookup was successful */
361         else
362           rh->name_callback (rh->cls, NULL);
363       }
364       if (NULL != rh->addr_callback)
365         rh->addr_callback (rh->cls, NULL, 0);
366     }
367     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
368     GNUNET_free (rh);
369     GNUNET_CLIENT_disconnect (client);
370     client = NULL;
371     reconnect ();
372     return;
373   }
374   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
375   {
376     GNUNET_break (0);
377     GNUNET_CLIENT_disconnect (client);
378     client = NULL;
379     reconnect ();
380     return;
381   }
382   size = ntohs (msg->size);
383   /* message contains not data, just header */
384   if (size == sizeof (struct GNUNET_MessageHeader))
385   {
386     /* check if request was canceled */
387     if (rh->was_transmitted != GNUNET_SYSERR)
388     {
389       if (NULL != rh->name_callback)
390         rh->name_callback (rh->cls, NULL);
391       if (NULL != rh->addr_callback)
392         rh->addr_callback (rh->cls, NULL, 0);
393     }
394     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
395     GNUNET_free (rh);
396     process_requests ();
397     return;
398   }
399   /* return reverse lookup results to caller */
400   if (NULL != rh->name_callback)
401   {
402     const char *hostname;
403
404     hostname = (const char *) &msg[1];
405     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
406     {
407       GNUNET_break (0);
408       if (rh->was_transmitted != GNUNET_SYSERR)
409         rh->name_callback (rh->cls, NULL);
410       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
411       GNUNET_free (rh);
412       GNUNET_CLIENT_disconnect (client);
413       client = NULL;
414       reconnect ();
415       return;
416     }
417     LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s' for IP `%s'.\n",
418          hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
419     if (rh->was_transmitted != GNUNET_SYSERR)
420       rh->name_callback (rh->cls, hostname);
421     rh->received_response = GNUNET_YES;
422     GNUNET_CLIENT_receive (client, &handle_response, rh,
423                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
424   }
425   /* return lookup results to caller */
426   if (NULL != rh->addr_callback)
427   {
428     struct sockaddr_in v4;
429     struct sockaddr_in6 v6;
430     const struct sockaddr *sa;
431     socklen_t salen;
432     const void *ip;
433     size_t ip_len;
434
435     ip = &msg[1];
436     ip_len = size - sizeof (struct GNUNET_MessageHeader);
437     if (ip_len == sizeof (struct in_addr))
438     {
439       memset (&v4, 0, sizeof (v4));
440       v4.sin_family = AF_INET;
441       v4.sin_addr = *(struct in_addr*) ip;
442 #if HAVE_SOCKADDR_IN_SIN_LEN
443       v4.sin_len = sizeof (v4);
444 #endif
445       salen = sizeof (v4);
446       sa = (const struct sockaddr *) &v4;
447     }
448     else if (ip_len == sizeof (struct in6_addr))
449     {
450       memset (&v6, 0, sizeof (v6));
451       v6.sin6_family = AF_INET6;
452       v6.sin6_addr = *(struct in6_addr*) ip;
453 #if HAVE_SOCKADDR_IN_SIN_LEN
454       v6.sin6_len = sizeof (v6);
455 #endif
456       salen = sizeof (v6);
457       sa = (const struct sockaddr *) &v6;
458     }
459     else
460     {
461       GNUNET_break (0);
462       if (rh->was_transmitted != GNUNET_SYSERR)
463         rh->addr_callback (rh->cls, NULL, 0);
464       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
465       GNUNET_free (rh);
466       GNUNET_CLIENT_disconnect (client);
467       client = NULL;
468       reconnect ();
469       return;
470     }
471     rh->addr_callback (rh->cls, sa, salen);
472     GNUNET_CLIENT_receive (client, &handle_response, rh,
473                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
474   }
475 }
476
477
478 /**
479  * We've been asked to lookup the address for a hostname and were
480  * given a valid numeric string.  Perform the callbacks for the
481  * numeric addresses.
482  *
483  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
484  * @param tc unused scheduler context
485  */
486 static void
487 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
488 {
489   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
490   struct sockaddr_in v4;
491   struct sockaddr_in6 v6;
492   const char *hostname;
493
494   memset (&v4, 0, sizeof (v4));
495   v4.sin_family = AF_INET;
496 #if HAVE_SOCKADDR_IN_SIN_LEN
497   v4.sin_len = sizeof (v4);
498 #endif
499   memset (&v6, 0, sizeof (v6));
500   v6.sin6_family = AF_INET6;
501 #if HAVE_SOCKADDR_IN_SIN_LEN
502   v6.sin6_len = sizeof (v6);
503 #endif
504   hostname = (const char *) &rh[1];
505   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
506       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
507   {
508     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
509     if ((rh->af == AF_UNSPEC) &&
510         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
511     {
512       /* this can happen on some systems IF "hostname" is "localhost" */
513       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
514     }
515     rh->addr_callback (rh->cls, NULL, 0);
516     GNUNET_free (rh);
517     return;
518   }
519   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
520       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
521   {
522     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
523     rh->addr_callback (rh->cls, NULL, 0);
524     GNUNET_free (rh);
525     return;
526   }
527   /* why are we here? this task should not have been scheduled! */
528   GNUNET_assert (0);
529   GNUNET_free (rh);
530 }
531
532
533 /**
534  * We've been asked to lookup the address for a hostname and were
535  * given a variant of "loopback".  Perform the callbacks for the
536  * respective loopback numeric addresses.
537  *
538  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
539  * @param tc unused scheduler context
540  */
541 static void
542 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
543 {
544   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
545   struct sockaddr_in v4;
546   struct sockaddr_in6 v6;
547
548   memset (&v4, 0, sizeof (v4));
549   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
550   v4.sin_family = AF_INET;
551 #if HAVE_SOCKADDR_IN_SIN_LEN
552   v4.sin_len = sizeof (v4);
553 #endif
554   memset (&v6, 0, sizeof (v6));
555   v6.sin6_family = AF_INET6;
556 #if HAVE_SOCKADDR_IN_SIN_LEN
557   v6.sin6_len = sizeof (v6);
558 #endif
559   v6.sin6_addr = in6addr_loopback;
560   switch (rh->af)
561   {
562   case AF_INET:
563     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
564     break;
565   case AF_INET6:
566     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
567     break;
568   case AF_UNSPEC:
569     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
570     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
571     break;
572   default:
573     GNUNET_break (0);
574     break;
575   }
576   rh->addr_callback (rh->cls, NULL, 0);
577   GNUNET_free (rh);
578 }
579
580
581 /**
582  * Task executed on system shutdown.
583  */
584 static void
585 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
586 {
587   s_task = GNUNET_SCHEDULER_NO_TASK;
588   GNUNET_RESOLVER_disconnect ();
589 }
590
591
592 /**
593  * Process pending requests to the resolver.
594  */
595 static void
596 process_requests ()
597 {
598   struct GNUNET_RESOLVER_GetMessage *msg;
599   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
600   struct GNUNET_RESOLVER_RequestHandle *rh;
601
602   if (NULL == client)
603   {
604     reconnect ();
605     return;
606   }
607   rh = req_head;
608   if (NULL == rh)
609   {
610     /* nothing to do, release socket really soon if there is nothing
611      * else happening... */
612     s_task =
613         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->af = htonl (rh->af);
625   memcpy (&msg[1], &rh[1], rh->data_len);
626   LOG (GNUNET_ERROR_TYPE_DEBUG,
627        "Transmitting DNS resolution request to DNS service\n");
628   if (GNUNET_OK !=
629       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
630                                                GNUNET_TIME_absolute_get_remaining
631                                                (rh->timeout), GNUNET_YES,
632                                                &handle_response, rh))
633   {
634     GNUNET_CLIENT_disconnect (client);
635     client = NULL;
636     reconnect ();
637     return;
638   }
639   rh->was_transmitted = GNUNET_YES;
640 }
641
642
643 /**
644  * Now try to reconnect to the resolver service.
645  *
646  * @param cls NULL
647  * @param tc scheduler context
648  */
649 static void
650 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
651 {
652   r_task = GNUNET_SCHEDULER_NO_TASK;
653   if (NULL == req_head)
654     return;                     /* no work pending */
655   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
656     return;
657   LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
658   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
659   if (NULL == client)
660   {
661     LOG (GNUNET_ERROR_TYPE_DEBUG, "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   LOG (GNUNET_ERROR_TYPE_DEBUG,
702        "Will try to connect to DNS service in %s\n",
703        GNUNET_STRINGS_relative_time_to_string (backoff, GNUNET_YES));
704   GNUNET_assert (NULL != resolver_cfg);
705   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
706   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
707 }
708
709
710 /**
711  * Convert a string to one or more IP addresses.
712  *
713  * @param hostname the hostname to resolve
714  * @param af 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 af,
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->af = af;
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        ((af == AF_INET) || (af == AF_UNSPEC))) ||
750       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
751        ((af == AF_INET6) || (af == 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 (rh->af, &rh[1], rh->data_len);
790   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolver returns `%s'.\n", result);
791   if (result != NULL)
792   {
793     rh->name_callback (rh->cls, result);
794     GNUNET_free (result);
795   }
796   rh->name_callback (rh->cls, NULL);
797   GNUNET_free (rh);
798 }
799
800
801 /**
802  * Get an IP address as a string.
803  *
804  * @param sa host address
805  * @param salen length of host address
806  * @param do_resolve use GNUNET_NO to return numeric hostname
807  * @param timeout how long to try resolving
808  * @param callback function to call with hostnames
809  *        last callback is NULL when finished
810  * @param cls closure for callback
811  * @return handle that can be used to cancel the request
812  */
813 struct GNUNET_RESOLVER_RequestHandle *
814 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
815                               int do_resolve,
816                               struct GNUNET_TIME_Relative timeout,
817                               GNUNET_RESOLVER_HostnameCallback callback,
818                               void *cls)
819 {
820   struct GNUNET_RESOLVER_RequestHandle *rh;
821   size_t ip_len;
822   const void *ip;
823
824   check_config ();
825   switch (sa->sa_family)
826   {
827   case AF_INET:
828     ip_len = sizeof (struct in_addr);
829     ip = &((const struct sockaddr_in*)sa)->sin_addr;
830     break;
831   case AF_INET6:
832     ip_len = sizeof (struct in6_addr);
833     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
834     break;
835   default:
836     GNUNET_break (0);
837     return NULL;
838   }
839   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
840   rh->name_callback = callback;
841   rh->cls = cls;
842   rh->af = sa->sa_family;
843   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
844   memcpy (&rh[1], ip, ip_len);
845   rh->data_len = ip_len;
846   rh->direction = GNUNET_YES;
847   rh->received_response = GNUNET_NO;
848   if (GNUNET_NO == do_resolve)
849   {
850     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
851     return rh;
852   }
853   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
854   rh->was_queued = GNUNET_YES;
855   if (s_task != GNUNET_SCHEDULER_NO_TASK)
856   {
857     GNUNET_SCHEDULER_cancel (s_task);
858     s_task = GNUNET_SCHEDULER_NO_TASK;
859   }
860   process_requests ();
861   return rh;
862 }
863
864
865 /**
866  * Get local fully qualified af name
867  *
868  * @return fqdn
869  */
870 char *
871 GNUNET_RESOLVER_local_fqdn_get ()
872 {
873   struct hostent *host;
874   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
875
876   if (0 != gethostname (hostname, sizeof (hostname) - 1))
877   {
878     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
879                   "gethostname");
880     return NULL;
881   }
882   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our FQDN `%s'\n", hostname);
883   host = gethostbyname (hostname);
884   if (NULL == host)
885   {
886     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
887          hstrerror (h_errno));
888     return NULL;
889   }
890   return GNUNET_strdup (host->h_name);
891 }
892
893
894 /**
895  * Looking our own hostname.
896  *
897  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
898  * @param callback function to call with addresses
899  * @param cls closure for callback
900  * @param timeout how long to try resolving
901  * @return handle that can be used to cancel the request, NULL on error
902  */
903 struct GNUNET_RESOLVER_RequestHandle *
904 GNUNET_RESOLVER_hostname_resolve (int af,
905                                   struct GNUNET_TIME_Relative timeout,
906                                   GNUNET_RESOLVER_AddressCallback callback,
907                                   void *cls)
908 {
909   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
910
911   if (0 != gethostname (hostname, sizeof (hostname) - 1))
912   {
913     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
914                   "gethostname");
915     return NULL;
916   }
917   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our hostname `%s'\n", hostname);
918   return GNUNET_RESOLVER_ip_get (hostname, af, 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 */