expand GNUNET_OS_ProjectData API to also enable de-duplcation of logic for --help
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2015 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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 struct GNUNET_SCHEDULER_Task *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 struct GNUNET_SCHEDULER_Task *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 making reply callbacks in numeric lookups
132    * asynchronous, and for timeout handling.
133    */
134   struct GNUNET_SCHEDULER_Task * task;
135
136   /**
137    * Desired address family.
138    */
139   int af;
140
141   /**
142    * Has this request been transmitted to the service?
143    * #GNUNET_YES if transmitted
144    * #GNUNET_YES if not transmitted
145    * #GNUNET_SYSERR when request was canceled
146    */
147   int was_transmitted;
148
149   /**
150    * Did we add this request to the queue?
151    */
152   int was_queued;
153
154   /**
155    * Desired direction (IP to name or name to IP)
156    */
157   int direction;
158
159   /**
160    * #GNUNET_YES if a response was received
161    */
162   int received_response;
163
164   /**
165    * Length of the data that follows this struct.
166    */
167   size_t data_len;
168 };
169
170
171 /**
172  * Check that the resolver service runs on localhost
173  * (or equivalent).
174  *
175  * @return #GNUNET_OK if the resolver is properly configured,
176  *         #GNUNET_SYSERR otherwise.
177  */
178 static int
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,
199                                              "resolver",
200                                              "HOSTNAME",
201                                              &hostname))
202   {
203     LOG (GNUNET_ERROR_TYPE_INFO,
204          _("Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"),
205          "HOSTNAME",
206          "resolver");
207     return GNUNET_SYSERR;
208   }
209   if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
210       (1 != inet_pton (AF_INET6, hostname, &v6)))
211   {
212     GNUNET_free (hostname);
213     return GNUNET_SYSERR;
214   }
215   i = 0;
216   while (NULL != loopback[i])
217     if (0 == strcasecmp (loopback[i++], hostname))
218     {
219       GNUNET_free (hostname);
220       return GNUNET_OK;
221     }
222   LOG (GNUNET_ERROR_TYPE_INFO,
223        _("Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"),
224        "localhost",
225        "HOSTNAME",
226        "resolver");
227   GNUNET_free (hostname);
228   return GNUNET_SYSERR;
229 }
230
231
232 /**
233  * Create the connection to the resolver service.
234  *
235  * @param cfg configuration to use
236  */
237 void
238 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
239 {
240   GNUNET_assert (NULL != cfg);
241   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
242   resolver_cfg = cfg;
243   (void) check_config ();
244 }
245
246
247 /**
248  * Destroy the connection to the resolver service.
249  */
250 void
251 GNUNET_RESOLVER_disconnect ()
252 {
253   GNUNET_assert (NULL == req_head);
254   GNUNET_assert (NULL == req_tail);
255   if (NULL != client)
256   {
257     LOG (GNUNET_ERROR_TYPE_DEBUG,
258          "Disconnecting from DNS service\n");
259     GNUNET_CLIENT_disconnect (client);
260     client = NULL;
261   }
262   if (NULL != r_task)
263   {
264     GNUNET_SCHEDULER_cancel (r_task);
265     r_task = NULL;
266   }
267   if (NULL != s_task)
268   {
269     GNUNET_SCHEDULER_cancel (s_task);
270     s_task = NULL;
271   }
272 }
273
274
275 /**
276  * Convert IP address to string without DNS resolution.
277  *
278  * @param af address family
279  * @param ip the address
280  * @param ip_len number of bytes in @a ip
281  * @return address as a string, NULL on error
282  */
283 static char *
284 no_resolve (int af,
285             const void *ip, socklen_t ip_len)
286 {
287   char buf[INET6_ADDRSTRLEN];
288
289   switch (af)
290   {
291   case AF_INET:
292     if (ip_len != sizeof (struct in_addr))
293       return NULL;
294     if (NULL ==
295         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
296     {
297       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
298                     "inet_ntop");
299       return NULL;
300     }
301     break;
302   case AF_INET6:
303     if (ip_len != sizeof (struct in6_addr))
304       return NULL;
305     if (NULL ==
306         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
307     {
308       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
309                     "inet_ntop");
310       return NULL;
311     }
312     break;
313   default:
314     GNUNET_break (0);
315     return NULL;
316   }
317   return GNUNET_strdup (buf);
318 }
319
320
321 /**
322  * Adjust exponential back-off and reconnect to the service.
323  */
324 static void
325 reconnect (void);
326
327
328 /**
329  * Process pending requests to the resolver.
330  */
331 static void
332 process_requests (void);
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,
343                  const struct GNUNET_MessageHeader *msg)
344 {
345   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
346   uint16_t size;
347   char *nret;
348
349   LOG (GNUNET_ERROR_TYPE_DEBUG,
350        "Receiving response from DNS service\n");
351   if (NULL == msg)
352   {
353     char buf[INET6_ADDRSTRLEN];
354
355     if (NULL != rh->name_callback)
356       LOG (GNUNET_ERROR_TYPE_INFO,
357            _("Timeout trying to resolve IP address `%s'.\n"),
358            inet_ntop (rh->af,
359                       (const void *) &rh[1],
360                       buf,
361                       sizeof(buf)));
362     else
363       LOG (GNUNET_ERROR_TYPE_INFO,
364            _("Timeout trying to resolve hostname `%s'.\n"),
365            (const char *) &rh[1]);
366     /* check if request was canceled */
367     if (GNUNET_SYSERR != rh->was_transmitted)
368     {
369       if (NULL != rh->name_callback)
370       {
371         /* no reverse lookup was successful, return IP as string */
372         if (GNUNET_NO == rh->received_response)
373         {
374           nret = no_resolve (rh->af,
375                              &rh[1],
376                              rh->data_len);
377           rh->name_callback (rh->cls, nret);
378           GNUNET_free (nret);
379         }
380         /* finally, make termination call */
381         rh->name_callback (rh->cls,
382                            NULL);
383       }
384       if (NULL != rh->addr_callback)
385         rh->addr_callback (rh->cls,
386                            NULL,
387                            0);
388     }
389     rh->was_transmitted = GNUNET_NO;
390     GNUNET_RESOLVER_request_cancel (rh);
391     GNUNET_CLIENT_disconnect (client);
392     client = NULL;
393     reconnect ();
394     return;
395   }
396   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
397   {
398     GNUNET_break (0);
399     GNUNET_CLIENT_disconnect (client);
400     client = NULL;
401     reconnect ();
402     return;
403   }
404   size = ntohs (msg->size);
405   if (size == sizeof (struct GNUNET_MessageHeader))
406   {
407     LOG (GNUNET_ERROR_TYPE_DEBUG,
408          "Received empty response from DNS service\n");
409     /* message contains not data, just header; end of replies */
410     /* check if request was canceled */
411     if (GNUNET_SYSERR != rh->was_transmitted)
412     {
413       /* no reverse lookup was successful, return IP as string */
414       if (NULL != rh->name_callback)
415       {
416         if (GNUNET_NO == rh->received_response)
417         {
418           nret = no_resolve (rh->af,
419                              &rh[1],
420                              rh->data_len);
421           rh->name_callback (rh->cls, nret);
422           GNUNET_free (nret);
423         }
424         /* finally, make termination call */
425         rh->name_callback (rh->cls,
426                            NULL);
427       }
428       if (NULL != rh->addr_callback)
429         rh->addr_callback (rh->cls,
430                            NULL,
431                            0);
432     }
433     rh->was_transmitted = GNUNET_NO;
434     GNUNET_RESOLVER_request_cancel (rh);
435     process_requests ();
436     return;
437   }
438   /* return reverse lookup results to caller */
439   if (NULL != rh->name_callback)
440   {
441     const char *hostname;
442
443     hostname = (const char *) &msg[1];
444     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
445     {
446       GNUNET_break (0);
447       if (GNUNET_SYSERR != rh->was_transmitted)
448         rh->name_callback (rh->cls,
449                            NULL);
450       rh->was_transmitted = GNUNET_NO;
451       GNUNET_RESOLVER_request_cancel (rh);
452       GNUNET_CLIENT_disconnect (client);
453       client = NULL;
454       reconnect ();
455       return;
456     }
457     LOG (GNUNET_ERROR_TYPE_DEBUG,
458          "Resolver returns `%s' for IP `%s'.\n",
459          hostname,
460          GNUNET_a2s ((const void *) &rh[1],
461                      rh->data_len));
462     if (rh->was_transmitted != GNUNET_SYSERR)
463       rh->name_callback (rh->cls,
464                          hostname);
465     rh->received_response = GNUNET_YES;
466   }
467   /* return lookup results to caller */
468   if (NULL != rh->addr_callback)
469   {
470     struct sockaddr_in v4;
471     struct sockaddr_in6 v6;
472     const struct sockaddr *sa;
473     socklen_t salen;
474     const void *ip;
475     size_t ip_len;
476
477     ip = &msg[1];
478     ip_len = size - sizeof (struct GNUNET_MessageHeader);
479     if (ip_len == sizeof (struct in_addr))
480     {
481       memset (&v4, 0, sizeof (v4));
482       v4.sin_family = AF_INET;
483       v4.sin_addr = *(struct in_addr*) ip;
484 #if HAVE_SOCKADDR_IN_SIN_LEN
485       v4.sin_len = sizeof (v4);
486 #endif
487       salen = sizeof (v4);
488       sa = (const struct sockaddr *) &v4;
489     }
490     else if (ip_len == sizeof (struct in6_addr))
491     {
492       memset (&v6, 0, sizeof (v6));
493       v6.sin6_family = AF_INET6;
494       v6.sin6_addr = *(struct in6_addr*) ip;
495 #if HAVE_SOCKADDR_IN_SIN_LEN
496       v6.sin6_len = sizeof (v6);
497 #endif
498       salen = sizeof (v6);
499       sa = (const struct sockaddr *) &v6;
500     }
501     else
502     {
503       GNUNET_break (0);
504       if (GNUNET_SYSERR != rh->was_transmitted)
505         rh->addr_callback (rh->cls,
506                            NULL,
507                            0);
508       rh->was_transmitted = GNUNET_NO;
509       GNUNET_RESOLVER_request_cancel (rh);
510       GNUNET_CLIENT_disconnect (client);
511       client = NULL;
512       reconnect ();
513       return;
514     }
515     LOG (GNUNET_ERROR_TYPE_DEBUG,
516          "Received IP from DNS service\n");
517     if (GNUNET_SYSERR != rh->was_transmitted)
518       rh->addr_callback (rh->cls,
519                          sa,
520                          salen);
521   }
522   GNUNET_CLIENT_receive (client,
523                          &handle_response,
524                          rh,
525                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
526 }
527
528
529 /**
530  * We've been asked to lookup the address for a hostname and were
531  * given a valid numeric string.  Perform the callbacks for the
532  * numeric addresses.
533  *
534  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
535  * @param tc unused scheduler context
536  */
537 static void
538 numeric_resolution (void *cls,
539                     const struct GNUNET_SCHEDULER_TaskContext *tc)
540 {
541   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
542   struct sockaddr_in v4;
543   struct sockaddr_in6 v6;
544   const char *hostname;
545
546   rh->task = NULL;
547   memset (&v4, 0, sizeof (v4));
548   v4.sin_family = AF_INET;
549 #if HAVE_SOCKADDR_IN_SIN_LEN
550   v4.sin_len = sizeof (v4);
551 #endif
552   memset (&v6, 0, sizeof (v6));
553   v6.sin6_family = AF_INET6;
554 #if HAVE_SOCKADDR_IN_SIN_LEN
555   v6.sin6_len = sizeof (v6);
556 #endif
557   hostname = (const char *) &rh[1];
558   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
559       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
560   {
561     rh->addr_callback (rh->cls,
562                        (const struct sockaddr *) &v4,
563                        sizeof (v4));
564     if ((rh->af == AF_UNSPEC) &&
565         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
566     {
567       /* this can happen on some systems IF "hostname" is "localhost" */
568       rh->addr_callback (rh->cls,
569                          (const struct sockaddr *) &v6,
570                          sizeof (v6));
571     }
572     rh->addr_callback (rh->cls,
573                        NULL,
574                        0);
575     GNUNET_free (rh);
576     return;
577   }
578   if ( ( (rh->af == AF_UNSPEC) ||
579          (rh->af == AF_INET6) ) &&
580        (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr) ) )
581   {
582     rh->addr_callback (rh->cls,
583                        (const struct sockaddr *) &v6,
584                        sizeof (v6));
585     rh->addr_callback (rh->cls,
586                        NULL,
587                        0);
588     GNUNET_free (rh);
589     return;
590   }
591   /* why are we here? this task should not have been scheduled! */
592   GNUNET_assert (0);
593   GNUNET_free (rh);
594 }
595
596
597 /**
598  * We've been asked to lookup the address for a hostname and were
599  * given a variant of "loopback".  Perform the callbacks for the
600  * respective loopback numeric addresses.
601  *
602  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
603  * @param tc unused scheduler context
604  */
605 static void
606 loopback_resolution (void *cls,
607                      const struct GNUNET_SCHEDULER_TaskContext *tc)
608 {
609   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
610   struct sockaddr_in v4;
611   struct sockaddr_in6 v6;
612
613   rh->task = NULL;
614   memset (&v4, 0, sizeof (v4));
615   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
616   v4.sin_family = AF_INET;
617 #if HAVE_SOCKADDR_IN_SIN_LEN
618   v4.sin_len = sizeof (v4);
619 #endif
620   memset (&v6, 0, sizeof (v6));
621   v6.sin6_family = AF_INET6;
622 #if HAVE_SOCKADDR_IN_SIN_LEN
623   v6.sin6_len = sizeof (v6);
624 #endif
625   v6.sin6_addr = in6addr_loopback;
626   switch (rh->af)
627   {
628   case AF_INET:
629     rh->addr_callback (rh->cls,
630                        (const struct sockaddr *) &v4,
631                        sizeof (v4));
632     break;
633   case AF_INET6:
634     rh->addr_callback (rh->cls,
635                        (const struct sockaddr *) &v6,
636                        sizeof (v6));
637     break;
638   case AF_UNSPEC:
639     rh->addr_callback (rh->cls,
640                        (const struct sockaddr *) &v6,
641                        sizeof (v6));
642     rh->addr_callback (rh->cls,
643                        (const struct sockaddr *) &v4,
644                        sizeof (v4));
645     break;
646   default:
647     GNUNET_break (0);
648     break;
649   }
650   rh->addr_callback (rh->cls,
651                      NULL,
652                      0);
653   GNUNET_free (rh);
654 }
655
656
657 /**
658  * Task executed on system shutdown.
659  */
660 static void
661 shutdown_task (void *cls,
662                const struct GNUNET_SCHEDULER_TaskContext *tc)
663 {
664   s_task = NULL;
665   GNUNET_RESOLVER_disconnect ();
666   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
667 }
668
669
670 /**
671  * Process pending requests to the resolver.
672  */
673 static void
674 process_requests ()
675 {
676   struct GNUNET_RESOLVER_GetMessage *msg;
677   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
678   struct GNUNET_RESOLVER_RequestHandle *rh;
679
680   if (NULL == client)
681   {
682     reconnect ();
683     return;
684   }
685   rh = req_head;
686   if (NULL == rh)
687   {
688     /* nothing to do, release socket really soon if there is nothing
689      * else happening... */
690     s_task =
691         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
692                                       &shutdown_task,
693                                       NULL);
694     return;
695   }
696   if (GNUNET_NO != rh->was_transmitted)
697     return;                     /* waiting for reply */
698   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
699   msg->header.size =
700       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
701   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
702   msg->direction = htonl (rh->direction);
703   msg->af = htonl (rh->af);
704   memcpy (&msg[1],
705           &rh[1],
706           rh->data_len);
707   LOG (GNUNET_ERROR_TYPE_DEBUG,
708        "Transmitting DNS resolution request to DNS service\n");
709   if (GNUNET_OK !=
710       GNUNET_CLIENT_transmit_and_get_response (client,
711                                                &msg->header,
712                                                GNUNET_TIME_absolute_get_remaining (rh->timeout),
713                                                GNUNET_YES,
714                                                &handle_response,
715                                                rh))
716   {
717     GNUNET_CLIENT_disconnect (client);
718     client = NULL;
719     GNUNET_break (0);
720     reconnect ();
721     return;
722   }
723   rh->was_transmitted = GNUNET_YES;
724 }
725
726
727 /**
728  * Now try to reconnect to the resolver service.
729  *
730  * @param cls NULL
731  * @param tc scheduler context
732  */
733 static void
734 reconnect_task (void *cls,
735                 const struct GNUNET_SCHEDULER_TaskContext *tc)
736 {
737   r_task = NULL;
738   if (NULL == req_head)
739     return;                     /* no work pending */
740   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
741     return;
742   LOG (GNUNET_ERROR_TYPE_DEBUG,
743        "Trying to connect to DNS service\n");
744   client = GNUNET_CLIENT_connect ("resolver",
745                                   resolver_cfg);
746   if (NULL == client)
747   {
748     LOG (GNUNET_ERROR_TYPE_DEBUG,
749          "Failed to connect, will try again later\n");
750     reconnect ();
751     return;
752   }
753   process_requests ();
754 }
755
756
757 /**
758  * Adjust exponential back-off and reconnect to the service.
759  */
760 static void
761 reconnect ()
762 {
763   struct GNUNET_RESOLVER_RequestHandle *rh;
764
765   if (NULL != r_task)
766     return;
767   GNUNET_assert (NULL == client);
768   if (NULL != (rh = req_head))
769   {
770     switch (rh->was_transmitted)
771     {
772     case GNUNET_NO:
773       /* nothing more to do */
774       break;
775     case GNUNET_YES:
776       /* disconnected, transmit again! */
777       rh->was_transmitted = GNUNET_NO;
778       break;
779     case GNUNET_SYSERR:
780       /* request was cancelled, remove entirely */
781       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
782       GNUNET_free (rh);
783       break;
784     default:
785       GNUNET_assert (0);
786       break;
787     }
788   }
789   LOG (GNUNET_ERROR_TYPE_DEBUG,
790        "Will try to connect to DNS service in %s\n",
791        GNUNET_STRINGS_relative_time_to_string (backoff,
792                                                GNUNET_YES));
793   GNUNET_assert (NULL != resolver_cfg);
794   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
795                                          &reconnect_task,
796                                          NULL);
797   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
798 }
799
800
801 /**
802  * A DNS resolution timed out. Notify the application.
803  *
804  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
805  * @param tc scheduler context
806  */
807 static void
808 handle_lookup_timeout (void *cls,
809                        const struct GNUNET_SCHEDULER_TaskContext *tc)
810 {
811   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
812
813   rh->task = NULL;
814   rh->addr_callback (rh->cls,
815                      NULL,
816                      0);
817   GNUNET_RESOLVER_request_cancel (rh);
818 }
819
820
821 /**
822  * Convert a string to one or more IP addresses.
823  *
824  * @param hostname the hostname to resolve
825  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
826  * @param callback function to call with addresses
827  * @param callback_cls closure for @a callback
828  * @param timeout how long to try resolving
829  * @return handle that can be used to cancel the request, NULL on error
830  */
831 struct GNUNET_RESOLVER_RequestHandle *
832 GNUNET_RESOLVER_ip_get (const char *hostname,
833                         int af,
834                         struct GNUNET_TIME_Relative timeout,
835                         GNUNET_RESOLVER_AddressCallback callback,
836                         void *callback_cls)
837 {
838   struct GNUNET_RESOLVER_RequestHandle *rh;
839   size_t slen;
840   unsigned int i;
841   struct in_addr v4;
842   struct in6_addr v6;
843
844   slen = strlen (hostname) + 1;
845   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
846       GNUNET_SERVER_MAX_MESSAGE_SIZE)
847   {
848     GNUNET_break (0);
849     return NULL;
850   }
851   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
852   rh->af = af;
853   rh->addr_callback = callback;
854   rh->cls = callback_cls;
855   memcpy (&rh[1],
856           hostname,
857           slen);
858   rh->data_len = slen;
859   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
860   rh->direction = GNUNET_NO;
861   /* first, check if this is a numeric address */
862   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
863        ((af == AF_INET) || (af == AF_UNSPEC))) ||
864       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
865        ((af == AF_INET6) || (af == AF_UNSPEC))))
866   {
867     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
868                                          rh);
869     return rh;
870   }
871   /* then, check if this is a loopback address */
872   i = 0;
873   while (NULL != loopback[i])
874     if (0 == strcasecmp (loopback[i++],
875                          hostname))
876     {
877       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
878                                            rh);
879       return rh;
880     }
881   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
882                                            &handle_lookup_timeout,
883                                            rh);
884   GNUNET_CONTAINER_DLL_insert_tail (req_head,
885                                     req_tail,
886                                     rh);
887   rh->was_queued = GNUNET_YES;
888   if (NULL != s_task)
889   {
890     GNUNET_SCHEDULER_cancel (s_task);
891     s_task = NULL;
892   }
893   process_requests ();
894   return rh;
895 }
896
897
898 /**
899  * We've been asked to convert an address to a string without
900  * a reverse lookup, either because the client asked for it
901  * or because the DNS lookup hit a timeout.  Do the numeric
902  * conversion and invoke the callback.
903  *
904  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
905  * @param tc unused scheduler context
906  */
907 static void
908 numeric_reverse (void *cls,
909                  const struct GNUNET_SCHEDULER_TaskContext *tc)
910 {
911   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
912   char *result;
913
914   rh->task = NULL;
915   result = no_resolve (rh->af,
916                        &rh[1],
917                        rh->data_len);
918   LOG (GNUNET_ERROR_TYPE_DEBUG,
919        "Resolver returns `%s'.\n",
920        result);
921   if (NULL != result)
922   {
923     rh->name_callback (rh->cls,
924                        result);
925     GNUNET_free (result);
926   }
927   rh->name_callback (rh->cls,
928                      NULL);
929   GNUNET_free (rh);
930 }
931
932
933 /**
934  * Get an IP address as a string.
935  *
936  * @param sa host address
937  * @param salen length of host address in @a sa
938  * @param do_resolve use #GNUNET_NO to return numeric hostname
939  * @param timeout how long to try resolving
940  * @param callback function to call with hostnames
941  *        last callback is NULL when finished
942  * @param cls closure for @a callback
943  * @return handle that can be used to cancel the request
944  */
945 struct GNUNET_RESOLVER_RequestHandle *
946 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
947                               socklen_t salen,
948                               int do_resolve,
949                               struct GNUNET_TIME_Relative timeout,
950                               GNUNET_RESOLVER_HostnameCallback callback,
951                               void *cls)
952 {
953   struct GNUNET_RESOLVER_RequestHandle *rh;
954   size_t ip_len;
955   const void *ip;
956
957   if (GNUNET_OK != check_config ())
958   {
959     LOG (GNUNET_ERROR_TYPE_ERROR,
960          _("Resolver not configured correctly.\n"));
961     return NULL;
962   }
963
964   switch (sa->sa_family)
965   {
966   case AF_INET:
967     GNUNET_assert (salen == sizeof (struct sockaddr_in));
968     ip_len = sizeof (struct in_addr);
969     ip = &((const struct sockaddr_in*)sa)->sin_addr;
970     break;
971   case AF_INET6:
972     GNUNET_assert (salen == sizeof (struct sockaddr_in6));
973     ip_len = sizeof (struct in6_addr);
974     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
975     break;
976   default:
977     GNUNET_break (0);
978     return NULL;
979   }
980   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
981   rh->name_callback = callback;
982   rh->cls = cls;
983   rh->af = sa->sa_family;
984   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
985   memcpy (&rh[1], ip, ip_len);
986   rh->data_len = ip_len;
987   rh->direction = GNUNET_YES;
988   rh->received_response = GNUNET_NO;
989   if (GNUNET_NO == do_resolve)
990   {
991     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
992     return rh;
993   }
994   GNUNET_CONTAINER_DLL_insert_tail (req_head,
995                                     req_tail,
996                                     rh);
997   rh->was_queued = GNUNET_YES;
998   if (NULL != s_task)
999   {
1000     GNUNET_SCHEDULER_cancel (s_task);
1001     s_task = NULL;
1002   }
1003   process_requests ();
1004   return rh;
1005 }
1006
1007
1008 /**
1009  * Get local fully qualified af name
1010  *
1011  * @return fqdn
1012  */
1013 char *
1014 GNUNET_RESOLVER_local_fqdn_get ()
1015 {
1016   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1017
1018   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1019   {
1020     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1021                   "gethostname");
1022     return NULL;
1023   }
1024   LOG (GNUNET_ERROR_TYPE_DEBUG,
1025        "Resolving our FQDN `%s'\n",
1026        hostname);
1027 #if HAVE_GETADDRINFO
1028   {
1029     struct addrinfo *ai;
1030     int ret;
1031     char *rval;
1032
1033     if (0 != (ret = getaddrinfo (hostname, NULL, NULL, &ai)))
1034     {
1035       LOG (GNUNET_ERROR_TYPE_ERROR,
1036            _("Could not resolve our FQDN: %s\n"),
1037            gai_strerror (ret));
1038       return NULL;
1039     }
1040     if (NULL != ai->ai_canonname)
1041       rval = GNUNET_strdup (ai->ai_canonname);
1042     else
1043       rval = GNUNET_strdup (hostname);
1044     freeaddrinfo (ai);
1045     return rval;
1046   }
1047 #elif HAVE_GETHOSTBYNAME2
1048   {
1049     struct hostent *host;
1050
1051     host = gethostbyname2 (hostname, AF_INET);
1052     if (NULL == host)
1053       host = gethostbyname2 (hostname, AF_INET6);
1054     if (NULL == host)
1055       {
1056         LOG (GNUNET_ERROR_TYPE_ERROR,
1057              _("Could not resolve our FQDN: %s\n"),
1058              hstrerror (h_errno));
1059         return NULL;
1060       }
1061     return GNUNET_strdup (host->h_name);
1062   }
1063 #elif HAVE_GETHOSTBYNAME
1064   {
1065     struct hostent *host;
1066
1067     host = gethostbyname (hostname);
1068     if (NULL == host)
1069       {
1070         LOG (GNUNET_ERROR_TYPE_ERROR,
1071              _("Could not resolve our FQDN: %s\n"),
1072              hstrerror (h_errno));
1073         return NULL;
1074       }
1075     return GNUNET_strdup (host->h_name);
1076   }
1077 #else
1078   /* fallback: just hope name is already FQDN */
1079   return GNUNET_strdup (hostname);
1080 #endif
1081 }
1082
1083
1084 /**
1085  * Looking our own hostname.
1086  *
1087  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1088  * @param timeout how long to try resolving
1089  * @param callback function to call with addresses
1090  * @param cls closure for @a callback
1091  * @return handle that can be used to cancel the request, NULL on error
1092  */
1093 struct GNUNET_RESOLVER_RequestHandle *
1094 GNUNET_RESOLVER_hostname_resolve (int af,
1095                                   struct GNUNET_TIME_Relative timeout,
1096                                   GNUNET_RESOLVER_AddressCallback callback,
1097                                   void *cls)
1098 {
1099   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1100
1101   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1102   {
1103     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1104                   "gethostname");
1105     return NULL;
1106   }
1107   LOG (GNUNET_ERROR_TYPE_DEBUG,
1108        "Resolving our hostname `%s'\n",
1109        hostname);
1110   return GNUNET_RESOLVER_ip_get (hostname,
1111                                  af,
1112                                  timeout,
1113                                  callback,
1114                                  cls);
1115 }
1116
1117
1118 /**
1119  * Cancel a request that is still pending with the resolver.
1120  * Note that a client MUST NOT cancel a request that has
1121  * been completed (i.e, the callback has been called to
1122  * signal timeout or the final result).
1123  *
1124  * @param rh handle of request to cancel
1125  */
1126 void
1127 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1128 {
1129   if (NULL != rh->task)
1130   {
1131     GNUNET_SCHEDULER_cancel (rh->task);
1132     rh->task = NULL;
1133   }
1134   if (GNUNET_NO == rh->was_transmitted)
1135   {
1136     if (GNUNET_YES == rh->was_queued)
1137       GNUNET_CONTAINER_DLL_remove (req_head,
1138                                    req_tail,
1139                                    rh);
1140     GNUNET_free (rh);
1141     return;
1142   }
1143   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1144   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1145 }
1146
1147
1148 /* end of resolver_api.c */