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