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