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