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