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