handle case where resolver operation is cancelled during success callback
[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         if (GNUNET_SYSERR != rh->was_transmitted)
504           rh->name_callback (rh->cls,
505                              NULL);
506       }
507       if ( (NULL != rh->addr_callback) &&
508            (GNUNET_SYSERR != rh->was_transmitted) )
509           rh->addr_callback (rh->cls,
510                            NULL,
511                            0);
512     }
513     rh->was_transmitted = GNUNET_NO;
514     GNUNET_RESOLVER_request_cancel (rh);
515     process_requests ();
516     return;
517   }
518   /* return reverse lookup results to caller */
519   if (NULL != rh->name_callback)
520   {
521     const char *hostname;
522
523     hostname = (const char *) &msg[1];
524     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
525     {
526       GNUNET_break (0);
527       if (GNUNET_SYSERR != rh->was_transmitted)
528         rh->name_callback (rh->cls,
529                            NULL);
530       rh->was_transmitted = GNUNET_NO;
531       GNUNET_RESOLVER_request_cancel (rh);
532       GNUNET_MQ_destroy (mq);
533       mq = NULL;
534       reconnect ();
535       return;
536     }
537     LOG (GNUNET_ERROR_TYPE_DEBUG,
538          "Resolver returns `%s' for IP `%s'.\n",
539          hostname,
540          GNUNET_a2s ((const void *) &rh[1],
541                      rh->data_len));
542     if (rh->was_transmitted != GNUNET_SYSERR)
543       rh->name_callback (rh->cls,
544                          hostname);
545     rh->received_response = GNUNET_YES;
546   }
547   /* return lookup results to caller */
548   if (NULL != rh->addr_callback)
549   {
550     struct sockaddr_in v4;
551     struct sockaddr_in6 v6;
552     const struct sockaddr *sa;
553     socklen_t salen;
554     const void *ip;
555     size_t ip_len;
556
557     ip = &msg[1];
558     ip_len = size - sizeof (struct GNUNET_MessageHeader);
559     if (ip_len == sizeof (struct in_addr))
560     {
561       memset (&v4, 0, sizeof (v4));
562       v4.sin_family = AF_INET;
563       v4.sin_addr = *(struct in_addr*) ip;
564 #if HAVE_SOCKADDR_IN_SIN_LEN
565       v4.sin_len = sizeof (v4);
566 #endif
567       salen = sizeof (v4);
568       sa = (const struct sockaddr *) &v4;
569     }
570     else if (ip_len == sizeof (struct in6_addr))
571     {
572       memset (&v6, 0, sizeof (v6));
573       v6.sin6_family = AF_INET6;
574       v6.sin6_addr = *(struct in6_addr*) ip;
575 #if HAVE_SOCKADDR_IN_SIN_LEN
576       v6.sin6_len = sizeof (v6);
577 #endif
578       salen = sizeof (v6);
579       sa = (const struct sockaddr *) &v6;
580     }
581     else
582     {
583       GNUNET_break (0);
584       if (GNUNET_SYSERR != rh->was_transmitted)
585         rh->addr_callback (rh->cls,
586                            NULL,
587                            0);
588       rh->was_transmitted = GNUNET_NO;
589       GNUNET_RESOLVER_request_cancel (rh);
590       GNUNET_MQ_destroy (mq);
591       mq = NULL;
592       reconnect ();
593       return;
594     }
595     LOG (GNUNET_ERROR_TYPE_DEBUG,
596          "Received IP from DNS service\n");
597     if (GNUNET_SYSERR != rh->was_transmitted)
598       rh->addr_callback (rh->cls,
599                          sa,
600                          salen);
601   }
602 }
603
604
605 /**
606  * We've been asked to lookup the address for a hostname and were
607  * given a valid numeric string.  Perform the callbacks for the
608  * numeric addresses.
609  *
610  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
611  */
612 static void
613 numeric_resolution (void *cls)
614 {
615   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
616   struct sockaddr_in v4;
617   struct sockaddr_in6 v6;
618   const char *hostname;
619
620   rh->task = NULL;
621   memset (&v4, 0, sizeof (v4));
622   v4.sin_family = AF_INET;
623 #if HAVE_SOCKADDR_IN_SIN_LEN
624   v4.sin_len = sizeof (v4);
625 #endif
626   memset (&v6, 0, sizeof (v6));
627   v6.sin6_family = AF_INET6;
628 #if HAVE_SOCKADDR_IN_SIN_LEN
629   v6.sin6_len = sizeof (v6);
630 #endif
631   hostname = (const char *) &rh[1];
632   if ( ( (rh->af == AF_UNSPEC) ||
633          (rh->af == AF_INET) ) &&
634        (1 == inet_pton (AF_INET,
635                         hostname,
636                         &v4.sin_addr)) )
637   {
638     rh->addr_callback (rh->cls,
639                        (const struct sockaddr *) &v4,
640                        sizeof (v4));
641     if ( (rh->af == AF_UNSPEC) &&
642          (GNUNET_SYSERR != rh->was_transmitted) &&
643          (1 == inet_pton (AF_INET6,
644                           hostname,
645                           &v6.sin6_addr)) )
646     {
647       /* this can happen on some systems IF "hostname" is "localhost" */
648       rh->addr_callback (rh->cls,
649                          (const struct sockaddr *) &v6,
650                          sizeof (v6));
651     }
652     if (GNUNET_SYSERR != rh->was_transmitted)
653       rh->addr_callback (rh->cls,
654                          NULL,
655                          0);
656     GNUNET_free (rh);
657     return;
658   }
659   if ( ( (rh->af == AF_UNSPEC) ||
660          (rh->af == AF_INET6) ) &&
661        (1 == inet_pton (AF_INET6,
662                         hostname,
663                         &v6.sin6_addr) ) )
664   {
665     rh->addr_callback (rh->cls,
666                        (const struct sockaddr *) &v6,
667                        sizeof (v6));
668     if (GNUNET_SYSERR != rh->was_transmitted)
669       rh->addr_callback (rh->cls,
670                          NULL,
671                          0);
672     GNUNET_free (rh);
673     return;
674   }
675   /* why are we here? this task should not have been scheduled! */
676   GNUNET_assert (0);
677   GNUNET_free (rh);
678 }
679
680
681 /**
682  * We've been asked to lookup the address for a hostname and were
683  * given a variant of "loopback".  Perform the callbacks for the
684  * respective loopback numeric addresses.
685  *
686  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
687  */
688 static void
689 loopback_resolution (void *cls)
690 {
691   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
692   struct sockaddr_in v4;
693   struct sockaddr_in6 v6;
694
695   rh->task = NULL;
696   memset (&v4, 0, sizeof (v4));
697   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
698   v4.sin_family = AF_INET;
699 #if HAVE_SOCKADDR_IN_SIN_LEN
700   v4.sin_len = sizeof (v4);
701 #endif
702   memset (&v6, 0, sizeof (v6));
703   v6.sin6_family = AF_INET6;
704 #if HAVE_SOCKADDR_IN_SIN_LEN
705   v6.sin6_len = sizeof (v6);
706 #endif
707   v6.sin6_addr = in6addr_loopback;
708   switch (rh->af)
709   {
710   case AF_INET:
711     rh->addr_callback (rh->cls,
712                        (const struct sockaddr *) &v4,
713                        sizeof (v4));
714     break;
715   case AF_INET6:
716     rh->addr_callback (rh->cls,
717                        (const struct sockaddr *) &v6,
718                        sizeof (v6));
719     break;
720   case AF_UNSPEC:
721     rh->addr_callback (rh->cls,
722                        (const struct sockaddr *) &v6,
723                        sizeof (v6));
724     rh->addr_callback (rh->cls,
725                        (const struct sockaddr *) &v4,
726                        sizeof (v4));
727
728     break;
729   default:
730     GNUNET_break (0);
731     break;
732   }
733   if (GNUNET_SYSERR != rh->was_transmitted)
734     rh->addr_callback (rh->cls,
735                        NULL,
736                        0);
737   LOG (GNUNET_ERROR_TYPE_DEBUG,
738        "Finished resolving hostname `%s'.\n",
739        (const char *) &rh[1]);
740   GNUNET_free (rh);
741 }
742
743
744 /**
745  * Now try to reconnect to the resolver service.
746  *
747  * @param cls NULL
748  */
749 static void
750 reconnect_task (void *cls)
751 {
752   struct GNUNET_MQ_MessageHandler handlers[] = {
753     GNUNET_MQ_hd_var_size (response,
754                            GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
755                            struct GNUNET_MessageHeader,
756                            NULL),
757     GNUNET_MQ_handler_end ()
758   };
759
760   (void) cls;
761   r_task = NULL;
762   if (NULL == req_head)
763     return;                     /* no work pending */
764   LOG (GNUNET_ERROR_TYPE_DEBUG,
765        "Trying to connect to DNS service\n");
766   mq = GNUNET_CLIENT_connect (resolver_cfg,
767                               "resolver",
768                               handlers,
769                               &mq_error_handler,
770                               NULL);
771   if (NULL == mq)
772   {
773     LOG (GNUNET_ERROR_TYPE_DEBUG,
774          "Failed to connect, will try again later\n");
775     reconnect ();
776     return;
777   }
778   process_requests ();
779 }
780
781
782 /**
783  * Adjust exponential back-off and reconnect to the service.
784  */
785 static void
786 reconnect ()
787 {
788   struct GNUNET_RESOLVER_RequestHandle *rh;
789
790   if (NULL != r_task)
791     return;
792   GNUNET_assert (NULL == mq);
793   if (NULL != (rh = req_head))
794   {
795     switch (rh->was_transmitted)
796     {
797     case GNUNET_NO:
798       /* nothing more to do */
799       break;
800     case GNUNET_YES:
801       /* disconnected, transmit again! */
802       rh->was_transmitted = GNUNET_NO;
803       break;
804     case GNUNET_SYSERR:
805       /* request was cancelled, remove entirely */
806       GNUNET_CONTAINER_DLL_remove (req_head,
807                                    req_tail,
808                                    rh);
809       GNUNET_free (rh);
810       check_disconnect ();
811       break;
812     default:
813       GNUNET_assert (0);
814       break;
815     }
816   }
817   LOG (GNUNET_ERROR_TYPE_DEBUG,
818        "Will try to connect to DNS service in %s\n",
819        GNUNET_STRINGS_relative_time_to_string (backoff,
820                                                GNUNET_YES));
821   GNUNET_assert (NULL != resolver_cfg);
822   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
823                                          &reconnect_task,
824                                          NULL);
825   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
826 }
827
828
829 /**
830  * A DNS resolution timed out. Notify the application.
831  *
832  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
833  */
834 static void
835 handle_lookup_timeout (void *cls)
836 {
837   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
838
839   rh->task = NULL;
840   if (GNUNET_NO == rh->direction)
841   {
842     LOG (GNUNET_ERROR_TYPE_INFO,
843          _("Timeout trying to resolve hostname `%s'.\n"),
844          (const char *) &rh[1]);
845     if (NULL != rh->addr_callback)
846       rh->addr_callback (rh->cls,
847                          NULL,
848                          0);
849   }
850   else
851   {
852 #if !defined(GNUNET_CULL_LOGGING)
853     char buf[INET6_ADDRSTRLEN];
854
855     LOG (GNUNET_ERROR_TYPE_INFO,
856          _("Timeout trying to resolve IP address `%s'.\n"),
857          inet_ntop (rh->af,
858                     (const void *) &rh[1],
859                     buf,
860                     sizeof(buf)));
861 #endif
862     if (GNUNET_NO == rh->received_response)
863     {
864       char *nret;
865
866       nret = no_resolve (rh->af,
867                          &rh[1],
868                          rh->data_len);
869       if (NULL != rh->name_callback)
870         rh->name_callback (rh->cls, nret);
871       GNUNET_free (nret);
872     }
873     /* finally, make termination call */
874     if (NULL != rh->name_callback)
875       rh->name_callback (rh->cls,
876                          NULL);
877   }
878   rh->was_transmitted = GNUNET_NO;
879   GNUNET_RESOLVER_request_cancel (rh);
880   process_requests ();
881 }
882
883
884 /**
885  * Convert a string to one or more IP addresses.
886  *
887  * @param hostname the hostname to resolve
888  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
889  * @param callback function to call with addresses
890  * @param callback_cls closure for @a callback
891  * @param timeout how long to try resolving
892  * @return handle that can be used to cancel the request, NULL on error
893  */
894 struct GNUNET_RESOLVER_RequestHandle *
895 GNUNET_RESOLVER_ip_get (const char *hostname,
896                         int af,
897                         struct GNUNET_TIME_Relative timeout,
898                         GNUNET_RESOLVER_AddressCallback callback,
899                         void *callback_cls)
900 {
901   struct GNUNET_RESOLVER_RequestHandle *rh;
902   size_t slen;
903   struct in_addr v4;
904   struct in6_addr v6;
905
906   slen = strlen (hostname) + 1;
907   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
908       GNUNET_MAX_MESSAGE_SIZE)
909   {
910     GNUNET_break (0);
911     return NULL;
912   }
913   LOG (GNUNET_ERROR_TYPE_DEBUG,
914        "Trying to resolve hostname `%s'.\n",
915        hostname);
916   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
917   rh->af = af;
918   rh->addr_callback = callback;
919   rh->cls = callback_cls;
920   GNUNET_memcpy (&rh[1],
921                  hostname,
922                  slen);
923   rh->data_len = slen;
924   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
925   rh->direction = GNUNET_NO;
926   /* first, check if this is a numeric address */
927   if ( ( (1 == inet_pton (AF_INET,
928                           hostname,
929                           &v4)) &&
930          ( (af == AF_INET) ||
931            (af == AF_UNSPEC) ) ) ||
932        ( (1 == inet_pton (AF_INET6,
933                           hostname,
934                           &v6)) &&
935          ( (af == AF_INET6) ||
936            (af == AF_UNSPEC)) ) )
937   {
938     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
939                                          rh);
940     return rh;
941   }
942   /* then, check if this is a loopback address */
943   for (unsigned int i = 0;
944        NULL != loopback[i];
945        i++)
946     if (0 == strcasecmp (loopback[i],
947                          hostname))
948     {
949       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
950                                            rh);
951       return rh;
952     }
953   if (GNUNET_OK != check_config ())
954   {
955     GNUNET_free (rh);
956     return NULL;
957   }
958   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
959                                            &handle_lookup_timeout,
960                                            rh);
961   GNUNET_CONTAINER_DLL_insert_tail (req_head,
962                                     req_tail,
963                                     rh);
964   rh->was_queued = GNUNET_YES;
965   if (NULL != s_task)
966   {
967     GNUNET_SCHEDULER_cancel (s_task);
968     s_task = NULL;
969   }
970   process_requests ();
971   return rh;
972 }
973
974
975 /**
976  * We've been asked to convert an address to a string without
977  * a reverse lookup, either because the client asked for it
978  * or because the DNS lookup hit a timeout.  Do the numeric
979  * conversion and invoke the callback.
980  *
981  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
982  */
983 static void
984 numeric_reverse (void *cls)
985 {
986   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
987   char *result;
988
989   rh->task = NULL;
990   result = no_resolve (rh->af,
991                        &rh[1],
992                        rh->data_len);
993   LOG (GNUNET_ERROR_TYPE_DEBUG,
994        "Resolver returns `%s'.\n",
995        result);
996   if (NULL != result)
997   {
998     rh->name_callback (rh->cls,
999                        result);
1000     GNUNET_free (result);
1001   }
1002   rh->name_callback (rh->cls,
1003                      NULL);
1004   if (NULL != rh->task)
1005   {
1006     GNUNET_SCHEDULER_cancel (rh->task);
1007     rh->task = NULL;
1008   }
1009   GNUNET_free (rh);
1010 }
1011
1012
1013 /**
1014  * Get an IP address as a string.
1015  *
1016  * @param sa host address
1017  * @param salen length of host address in @a sa
1018  * @param do_resolve use #GNUNET_NO to return numeric hostname
1019  * @param timeout how long to try resolving
1020  * @param callback function to call with hostnames
1021  *        last callback is NULL when finished
1022  * @param cls closure for @a callback
1023  * @return handle that can be used to cancel the request
1024  */
1025 struct GNUNET_RESOLVER_RequestHandle *
1026 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
1027                               socklen_t salen,
1028                               int do_resolve,
1029                               struct GNUNET_TIME_Relative timeout,
1030                               GNUNET_RESOLVER_HostnameCallback callback,
1031                               void *cls)
1032 {
1033   struct GNUNET_RESOLVER_RequestHandle *rh;
1034   size_t ip_len;
1035   const void *ip;
1036
1037   if (GNUNET_OK != check_config ())
1038   {
1039     LOG (GNUNET_ERROR_TYPE_ERROR,
1040          _("Resolver not configured correctly.\n"));
1041     return NULL;
1042   }
1043
1044   switch (sa->sa_family)
1045   {
1046   case AF_INET:
1047     GNUNET_assert (salen == sizeof (struct sockaddr_in));
1048     ip_len = sizeof (struct in_addr);
1049     ip = &((const struct sockaddr_in*)sa)->sin_addr;
1050     break;
1051   case AF_INET6:
1052     GNUNET_assert (salen == sizeof (struct sockaddr_in6));
1053     ip_len = sizeof (struct in6_addr);
1054     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
1055     break;
1056   default:
1057     GNUNET_break (0);
1058     return NULL;
1059   }
1060   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
1061   rh->name_callback = callback;
1062   rh->cls = cls;
1063   rh->af = sa->sa_family;
1064   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1065   GNUNET_memcpy (&rh[1],
1066                  ip,
1067                  ip_len);
1068   rh->data_len = ip_len;
1069   rh->direction = GNUNET_YES;
1070   rh->received_response = GNUNET_NO;
1071   if (GNUNET_NO == do_resolve)
1072   {
1073     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
1074                                          rh);
1075     return rh;
1076   }
1077   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1078                                            &handle_lookup_timeout,
1079                                            rh);
1080   GNUNET_CONTAINER_DLL_insert_tail (req_head,
1081                                     req_tail,
1082                                     rh);
1083   rh->was_queued = GNUNET_YES;
1084   if (NULL != s_task)
1085   {
1086     GNUNET_SCHEDULER_cancel (s_task);
1087     s_task = NULL;
1088   }
1089   process_requests ();
1090   return rh;
1091 }
1092
1093
1094 /**
1095  * Get local fully qualified af name
1096  *
1097  * @return fqdn
1098  */
1099 char *
1100 GNUNET_RESOLVER_local_fqdn_get ()
1101 {
1102   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1103
1104   if (0 != gethostname (hostname,
1105                         sizeof (hostname) - 1))
1106   {
1107     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1108                   "gethostname");
1109     return NULL;
1110   }
1111   LOG (GNUNET_ERROR_TYPE_DEBUG,
1112        "Resolving our FQDN `%s'\n",
1113        hostname);
1114 #if HAVE_GETADDRINFO
1115   {
1116     struct addrinfo *ai;
1117     int ret;
1118     char *rval;
1119
1120     if (0 != (ret = getaddrinfo (hostname,
1121                                  NULL,
1122                                  NULL,
1123                                  &ai)))
1124     {
1125       LOG (GNUNET_ERROR_TYPE_ERROR,
1126            _("Could not resolve our FQDN: %s\n"),
1127            gai_strerror (ret));
1128       return NULL;
1129     }
1130     if (NULL != ai->ai_canonname)
1131       rval = GNUNET_strdup (ai->ai_canonname);
1132     else
1133       rval = GNUNET_strdup (hostname);
1134     freeaddrinfo (ai);
1135     return rval;
1136   }
1137 #elif HAVE_GETHOSTBYNAME2
1138   {
1139     struct hostent *host;
1140
1141     host = gethostbyname2 (hostname,
1142                            AF_INET);
1143     if (NULL == host)
1144       host = gethostbyname2 (hostname,
1145                              AF_INET6);
1146     if (NULL == host)
1147       {
1148         LOG (GNUNET_ERROR_TYPE_ERROR,
1149              _("Could not resolve our FQDN: %s\n"),
1150              hstrerror (h_errno));
1151         return NULL;
1152       }
1153     return GNUNET_strdup (host->h_name);
1154   }
1155 #elif HAVE_GETHOSTBYNAME
1156   {
1157     struct hostent *host;
1158
1159     host = gethostbyname (hostname);
1160     if (NULL == host)
1161       {
1162         LOG (GNUNET_ERROR_TYPE_ERROR,
1163              _("Could not resolve our FQDN: %s\n"),
1164              hstrerror (h_errno));
1165         return NULL;
1166       }
1167     return GNUNET_strdup (host->h_name);
1168   }
1169 #else
1170   /* fallback: just hope name is already FQDN */
1171   return GNUNET_strdup (hostname);
1172 #endif
1173 }
1174
1175
1176 /**
1177  * Looking our own hostname.
1178  *
1179  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1180  * @param timeout how long to try resolving
1181  * @param callback function to call with addresses
1182  * @param cls closure for @a callback
1183  * @return handle that can be used to cancel the request, NULL on error
1184  */
1185 struct GNUNET_RESOLVER_RequestHandle *
1186 GNUNET_RESOLVER_hostname_resolve (int af,
1187                                   struct GNUNET_TIME_Relative timeout,
1188                                   GNUNET_RESOLVER_AddressCallback callback,
1189                                   void *cls)
1190 {
1191   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1192
1193   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1194   {
1195     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1196                   "gethostname");
1197     return NULL;
1198   }
1199   LOG (GNUNET_ERROR_TYPE_DEBUG,
1200        "Resolving our hostname `%s'\n",
1201        hostname);
1202   return GNUNET_RESOLVER_ip_get (hostname,
1203                                  af,
1204                                  timeout,
1205                                  callback,
1206                                  cls);
1207 }
1208
1209
1210 /**
1211  * Cancel a request that is still pending with the resolver.
1212  * Note that a client MUST NOT cancel a request that has
1213  * been completed (i.e, the callback has been called to
1214  * signal timeout or the final result).
1215  *
1216  * @param rh handle of request to cancel
1217  */
1218 void
1219 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1220 {
1221   if (GNUNET_NO == rh->direction)
1222     LOG (GNUNET_ERROR_TYPE_DEBUG,
1223          "Asked to cancel request to resolve hostname `%s'.\n",
1224          (const char *) &rh[1]);
1225   if (NULL != rh->task)
1226   {
1227     GNUNET_SCHEDULER_cancel (rh->task);
1228     rh->task = NULL;
1229   }
1230   if (GNUNET_NO == rh->was_transmitted)
1231   {
1232     if (GNUNET_YES == rh->was_queued)
1233       GNUNET_CONTAINER_DLL_remove (req_head,
1234                                    req_tail,
1235                                    rh);
1236     GNUNET_free (rh);
1237     check_disconnect ();
1238     return;
1239   }
1240   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1241   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1242   check_disconnect ();
1243 }
1244
1245
1246 /* end of resolver_api.c */