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