-disable NSE POW during cadet tests
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-2014 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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 GNUNET_SCHEDULER_TaskIdentifier 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 GNUNET_SCHEDULER_TaskIdentifier 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   GNUNET_SCHEDULER_TaskIdentifier 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 (GNUNET_SCHEDULER_NO_TASK != r_task)
260   {
261     GNUNET_SCHEDULER_cancel (r_task);
262     r_task = GNUNET_SCHEDULER_NO_TASK;
263   }
264   if (GNUNET_SCHEDULER_NO_TASK != s_task)
265   {
266     GNUNET_SCHEDULER_cancel (s_task);
267     s_task = GNUNET_SCHEDULER_NO_TASK;
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, NULL);
379       }
380       if (NULL != rh->addr_callback)
381         rh->addr_callback (rh->cls, NULL, 0);
382     }
383     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
384     if (GNUNET_SCHEDULER_NO_TASK != rh->task)
385       GNUNET_SCHEDULER_cancel (rh->task);
386     GNUNET_free (rh);
387     GNUNET_CLIENT_disconnect (client);
388     client = NULL;
389     reconnect ();
390     return;
391   }
392   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
393   {
394     GNUNET_break (0);
395     GNUNET_CLIENT_disconnect (client);
396     client = NULL;
397     reconnect ();
398     return;
399   }
400   size = ntohs (msg->size);
401   if (size == sizeof (struct GNUNET_MessageHeader))
402   {
403     /* message contains not data, just header; end of replies */
404     /* check if request was canceled */
405     if (GNUNET_SYSERR != rh->was_transmitted)
406     {
407       if (NULL != rh->name_callback)
408         rh->name_callback (rh->cls, NULL);
409       if (NULL != rh->addr_callback)
410         rh->addr_callback (rh->cls, NULL, 0);
411     }
412     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
413     if (GNUNET_SCHEDULER_NO_TASK != rh->task)
414       GNUNET_SCHEDULER_cancel (rh->task);
415     GNUNET_free (rh);
416     process_requests ();
417     return;
418   }
419   /* return reverse lookup results to caller */
420   if (NULL != rh->name_callback)
421   {
422     const char *hostname;
423
424     hostname = (const char *) &msg[1];
425     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
426     {
427       GNUNET_break (0);
428       if (GNUNET_SYSERR != rh->was_transmitted)
429         rh->name_callback (rh->cls, NULL);
430       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
431       if (GNUNET_SCHEDULER_NO_TASK != rh->task)
432         GNUNET_SCHEDULER_cancel (rh->task);
433       GNUNET_free (rh);
434       GNUNET_CLIENT_disconnect (client);
435       client = NULL;
436       reconnect ();
437       return;
438     }
439     LOG (GNUNET_ERROR_TYPE_DEBUG,
440          "Resolver returns `%s' for IP `%s'.\n",
441          hostname,
442          GNUNET_a2s ((const void *) &rh[1],
443                      rh->data_len));
444     if (rh->was_transmitted != GNUNET_SYSERR)
445       rh->name_callback (rh->cls, hostname);
446     rh->received_response = GNUNET_YES;
447   }
448   /* return lookup results to caller */
449   if (NULL != rh->addr_callback)
450   {
451     struct sockaddr_in v4;
452     struct sockaddr_in6 v6;
453     const struct sockaddr *sa;
454     socklen_t salen;
455     const void *ip;
456     size_t ip_len;
457
458     ip = &msg[1];
459     ip_len = size - sizeof (struct GNUNET_MessageHeader);
460     if (ip_len == sizeof (struct in_addr))
461     {
462       memset (&v4, 0, sizeof (v4));
463       v4.sin_family = AF_INET;
464       v4.sin_addr = *(struct in_addr*) ip;
465 #if HAVE_SOCKADDR_IN_SIN_LEN
466       v4.sin_len = sizeof (v4);
467 #endif
468       salen = sizeof (v4);
469       sa = (const struct sockaddr *) &v4;
470     }
471     else if (ip_len == sizeof (struct in6_addr))
472     {
473       memset (&v6, 0, sizeof (v6));
474       v6.sin6_family = AF_INET6;
475       v6.sin6_addr = *(struct in6_addr*) ip;
476 #if HAVE_SOCKADDR_IN_SIN_LEN
477       v6.sin6_len = sizeof (v6);
478 #endif
479       salen = sizeof (v6);
480       sa = (const struct sockaddr *) &v6;
481     }
482     else
483     {
484       GNUNET_break (0);
485       if (GNUNET_SYSERR != rh->was_transmitted)
486         rh->addr_callback (rh->cls, NULL, 0);
487       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
488       if (GNUNET_SCHEDULER_NO_TASK != rh->task)
489         GNUNET_SCHEDULER_cancel (rh->task);
490       GNUNET_free (rh);
491       GNUNET_CLIENT_disconnect (client);
492       client = NULL;
493       reconnect ();
494       return;
495     }
496     rh->addr_callback (rh->cls, sa, salen);
497   }
498   GNUNET_CLIENT_receive (client,
499                          &handle_response,
500                          rh,
501                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
502 }
503
504
505 /**
506  * We've been asked to lookup the address for a hostname and were
507  * given a valid numeric string.  Perform the callbacks for the
508  * numeric addresses.
509  *
510  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
511  * @param tc unused scheduler context
512  */
513 static void
514 numeric_resolution (void *cls,
515                     const struct GNUNET_SCHEDULER_TaskContext *tc)
516 {
517   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
518   struct sockaddr_in v4;
519   struct sockaddr_in6 v6;
520   const char *hostname;
521
522   rh->task = GNUNET_SCHEDULER_NO_TASK;
523   memset (&v4, 0, sizeof (v4));
524   v4.sin_family = AF_INET;
525 #if HAVE_SOCKADDR_IN_SIN_LEN
526   v4.sin_len = sizeof (v4);
527 #endif
528   memset (&v6, 0, sizeof (v6));
529   v6.sin6_family = AF_INET6;
530 #if HAVE_SOCKADDR_IN_SIN_LEN
531   v6.sin6_len = sizeof (v6);
532 #endif
533   hostname = (const char *) &rh[1];
534   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
535       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
536   {
537     rh->addr_callback (rh->cls,
538                        (const struct sockaddr *) &v4,
539                        sizeof (v4));
540     if ((rh->af == AF_UNSPEC) &&
541         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
542     {
543       /* this can happen on some systems IF "hostname" is "localhost" */
544       rh->addr_callback (rh->cls,
545                          (const struct sockaddr *) &v6,
546                          sizeof (v6));
547     }
548     rh->addr_callback (rh->cls, NULL, 0);
549     GNUNET_free (rh);
550     return;
551   }
552   if ( ( (rh->af == AF_UNSPEC) ||
553          (rh->af == AF_INET6) ) &&
554        (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr) ) )
555   {
556     rh->addr_callback (rh->cls,
557                        (const struct sockaddr *) &v6,
558                        sizeof (v6));
559     rh->addr_callback (rh->cls, NULL, 0);
560     GNUNET_free (rh);
561     return;
562   }
563   /* why are we here? this task should not have been scheduled! */
564   GNUNET_assert (0);
565   GNUNET_free (rh);
566 }
567
568
569 /**
570  * We've been asked to lookup the address for a hostname and were
571  * given a variant of "loopback".  Perform the callbacks for the
572  * respective loopback numeric addresses.
573  *
574  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
575  * @param tc unused scheduler context
576  */
577 static void
578 loopback_resolution (void *cls,
579                      const struct GNUNET_SCHEDULER_TaskContext *tc)
580 {
581   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
582   struct sockaddr_in v4;
583   struct sockaddr_in6 v6;
584
585   rh->task = GNUNET_SCHEDULER_NO_TASK;
586   memset (&v4, 0, sizeof (v4));
587   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
588   v4.sin_family = AF_INET;
589 #if HAVE_SOCKADDR_IN_SIN_LEN
590   v4.sin_len = sizeof (v4);
591 #endif
592   memset (&v6, 0, sizeof (v6));
593   v6.sin6_family = AF_INET6;
594 #if HAVE_SOCKADDR_IN_SIN_LEN
595   v6.sin6_len = sizeof (v6);
596 #endif
597   v6.sin6_addr = in6addr_loopback;
598   switch (rh->af)
599   {
600   case AF_INET:
601     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
602     break;
603   case AF_INET6:
604     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
605     break;
606   case AF_UNSPEC:
607     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
608     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
609     break;
610   default:
611     GNUNET_break (0);
612     break;
613   }
614   rh->addr_callback (rh->cls,
615                      NULL,
616                      0);
617   GNUNET_free (rh);
618 }
619
620
621 /**
622  * Task executed on system shutdown.
623  */
624 static void
625 shutdown_task (void *cls,
626                const struct GNUNET_SCHEDULER_TaskContext *tc)
627 {
628   s_task = GNUNET_SCHEDULER_NO_TASK;
629   GNUNET_RESOLVER_disconnect ();
630   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
631 }
632
633
634 /**
635  * Process pending requests to the resolver.
636  */
637 static void
638 process_requests ()
639 {
640   struct GNUNET_RESOLVER_GetMessage *msg;
641   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
642   struct GNUNET_RESOLVER_RequestHandle *rh;
643
644   if (NULL == client)
645   {
646     reconnect ();
647     return;
648   }
649   rh = req_head;
650   if (NULL == rh)
651   {
652     /* nothing to do, release socket really soon if there is nothing
653      * else happening... */
654     s_task =
655         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
656                                       &shutdown_task, NULL);
657     return;
658   }
659   if (GNUNET_YES == rh->was_transmitted)
660     return;                     /* waiting for reply */
661   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
662   msg->header.size =
663       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
664   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
665   msg->direction = htonl (rh->direction);
666   msg->af = htonl (rh->af);
667   memcpy (&msg[1], &rh[1], rh->data_len);
668   LOG (GNUNET_ERROR_TYPE_DEBUG,
669        "Transmitting DNS resolution request to DNS service\n");
670   if (GNUNET_OK !=
671       GNUNET_CLIENT_transmit_and_get_response (client,
672                                                &msg->header,
673                                                GNUNET_TIME_absolute_get_remaining (rh->timeout),
674                                                GNUNET_YES,
675                                                &handle_response, rh))
676   {
677     GNUNET_CLIENT_disconnect (client);
678     client = NULL;
679     GNUNET_break (0);
680     reconnect ();
681     return;
682   }
683   rh->was_transmitted = GNUNET_YES;
684 }
685
686
687 /**
688  * Now try to reconnect to the resolver service.
689  *
690  * @param cls NULL
691  * @param tc scheduler context
692  */
693 static void
694 reconnect_task (void *cls,
695                 const struct GNUNET_SCHEDULER_TaskContext *tc)
696 {
697   r_task = GNUNET_SCHEDULER_NO_TASK;
698   if (NULL == req_head)
699     return;                     /* no work pending */
700   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
701     return;
702   LOG (GNUNET_ERROR_TYPE_DEBUG,
703        "Trying to connect to DNS service\n");
704   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
705   if (NULL == client)
706   {
707     LOG (GNUNET_ERROR_TYPE_DEBUG,
708          "Failed to connect, will try again later\n");
709     reconnect ();
710     return;
711   }
712   process_requests ();
713 }
714
715
716 /**
717  * Adjust exponential back-off and reconnect to the service.
718  */
719 static void
720 reconnect ()
721 {
722   struct GNUNET_RESOLVER_RequestHandle *rh;
723
724   if (GNUNET_SCHEDULER_NO_TASK != r_task)
725     return;
726   GNUNET_assert (NULL == client);
727   if (NULL != (rh = req_head))
728   {
729     switch (rh->was_transmitted)
730     {
731     case GNUNET_NO:
732       /* nothing more to do */
733       break;
734     case GNUNET_YES:
735       /* disconnected, transmit again! */
736       rh->was_transmitted = GNUNET_NO;
737       break;
738     case GNUNET_SYSERR:
739       /* request was cancelled, remove entirely */
740       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
741       GNUNET_free (rh);
742       break;
743     default:
744       GNUNET_assert (0);
745       break;
746     }
747   }
748   LOG (GNUNET_ERROR_TYPE_DEBUG,
749        "Will try to connect to DNS service in %s\n",
750        GNUNET_STRINGS_relative_time_to_string (backoff, GNUNET_YES));
751   GNUNET_assert (NULL != resolver_cfg);
752   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
753   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
754 }
755
756
757 /**
758  * A DNS resolution timed out. Notify the application.
759  *
760  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
761  * @param tc scheduler context
762  */
763 static void
764 handle_lookup_timeout (void *cls,
765                        const struct GNUNET_SCHEDULER_TaskContext *tc)
766 {
767   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
768
769   rh->task = GNUNET_SCHEDULER_NO_TASK;
770   rh->addr_callback (rh->cls,
771                      NULL,
772                      0);
773   GNUNET_RESOLVER_request_cancel (rh);
774 }
775
776
777 /**
778  * Convert a string to one or more IP addresses.
779  *
780  * @param hostname the hostname to resolve
781  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
782  * @param callback function to call with addresses
783  * @param callback_cls closure for @a callback
784  * @param timeout how long to try resolving
785  * @return handle that can be used to cancel the request, NULL on error
786  */
787 struct GNUNET_RESOLVER_RequestHandle *
788 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
789                         struct GNUNET_TIME_Relative timeout,
790                         GNUNET_RESOLVER_AddressCallback callback,
791                         void *callback_cls)
792 {
793   struct GNUNET_RESOLVER_RequestHandle *rh;
794   size_t slen;
795   unsigned int i;
796   struct in_addr v4;
797   struct in6_addr v6;
798
799   slen = strlen (hostname) + 1;
800   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
801       GNUNET_SERVER_MAX_MESSAGE_SIZE)
802   {
803     GNUNET_break (0);
804     return NULL;
805   }
806   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
807   rh->af = af;
808   rh->addr_callback = callback;
809   rh->cls = callback_cls;
810   memcpy (&rh[1],
811           hostname,
812           slen);
813   rh->data_len = slen;
814   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
815   rh->direction = GNUNET_NO;
816   /* first, check if this is a numeric address */
817   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
818        ((af == AF_INET) || (af == AF_UNSPEC))) ||
819       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
820        ((af == AF_INET6) || (af == AF_UNSPEC))))
821   {
822     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
823                                          rh);
824     return rh;
825   }
826   /* then, check if this is a loopback address */
827   i = 0;
828   while (NULL != loopback[i])
829     if (0 == strcasecmp (loopback[i++], hostname))
830     {
831       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
832                                            rh);
833       return rh;
834     }
835   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
836                                            &handle_lookup_timeout,
837                                            rh);
838   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
839   rh->was_queued = GNUNET_YES;
840   if (s_task != GNUNET_SCHEDULER_NO_TASK)
841   {
842     GNUNET_SCHEDULER_cancel (s_task);
843     s_task = GNUNET_SCHEDULER_NO_TASK;
844   }
845   process_requests ();
846   return rh;
847 }
848
849
850 /**
851  * We've been asked to convert an address to a string without
852  * a reverse lookup, either because the client asked for it
853  * or because the DNS lookup hit a timeout.  Do the numeric
854  * conversion and invoke the callback.
855  *
856  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
857  * @param tc unused scheduler context
858  */
859 static void
860 numeric_reverse (void *cls,
861                  const struct GNUNET_SCHEDULER_TaskContext *tc)
862 {
863   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
864   char *result;
865
866   rh->task = GNUNET_SCHEDULER_NO_TASK;
867   result = no_resolve (rh->af,
868                        &rh[1],
869                        rh->data_len);
870   LOG (GNUNET_ERROR_TYPE_DEBUG,
871        "Resolver returns `%s'.\n",
872        result);
873   if (NULL != result)
874   {
875     rh->name_callback (rh->cls, result);
876     GNUNET_free (result);
877   }
878   rh->name_callback (rh->cls, NULL);
879   GNUNET_free (rh);
880 }
881
882
883 /**
884  * Get an IP address as a string.
885  *
886  * @param sa host address
887  * @param salen length of host address in @a sa
888  * @param do_resolve use #GNUNET_NO to return numeric hostname
889  * @param timeout how long to try resolving
890  * @param callback function to call with hostnames
891  *        last callback is NULL when finished
892  * @param cls closure for @a callback
893  * @return handle that can be used to cancel the request
894  */
895 struct GNUNET_RESOLVER_RequestHandle *
896 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
897                               socklen_t salen,
898                               int do_resolve,
899                               struct GNUNET_TIME_Relative timeout,
900                               GNUNET_RESOLVER_HostnameCallback callback,
901                               void *cls)
902 {
903   struct GNUNET_RESOLVER_RequestHandle *rh;
904   size_t ip_len;
905   const void *ip;
906
907   check_config ();
908   switch (sa->sa_family)
909   {
910   case AF_INET:
911     ip_len = sizeof (struct in_addr);
912     ip = &((const struct sockaddr_in*)sa)->sin_addr;
913     break;
914   case AF_INET6:
915     ip_len = sizeof (struct in6_addr);
916     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
917     break;
918   default:
919     GNUNET_break (0);
920     return NULL;
921   }
922   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
923   rh->name_callback = callback;
924   rh->cls = cls;
925   rh->af = sa->sa_family;
926   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
927   memcpy (&rh[1], ip, ip_len);
928   rh->data_len = ip_len;
929   rh->direction = GNUNET_YES;
930   rh->received_response = GNUNET_NO;
931   if (GNUNET_NO == do_resolve)
932   {
933     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
934     return rh;
935   }
936   GNUNET_CONTAINER_DLL_insert_tail (req_head,
937                                     req_tail,
938                                     rh);
939   rh->was_queued = GNUNET_YES;
940   if (s_task != GNUNET_SCHEDULER_NO_TASK)
941   {
942     GNUNET_SCHEDULER_cancel (s_task);
943     s_task = GNUNET_SCHEDULER_NO_TASK;
944   }
945   process_requests ();
946   return rh;
947 }
948
949
950 /**
951  * Get local fully qualified af name
952  *
953  * @return fqdn
954  */
955 char *
956 GNUNET_RESOLVER_local_fqdn_get ()
957 {
958   struct hostent *host;
959   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
960
961   if (0 != gethostname (hostname, sizeof (hostname) - 1))
962   {
963     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
964                   "gethostname");
965     return NULL;
966   }
967   LOG (GNUNET_ERROR_TYPE_DEBUG,
968        "Resolving our FQDN `%s'\n",
969        hostname);
970   host = gethostbyname (hostname);
971   if (NULL == host)
972   {
973     LOG (GNUNET_ERROR_TYPE_ERROR,
974          _("Could not resolve our FQDN : %s\n"),
975          hstrerror (h_errno));
976     return NULL;
977   }
978   return GNUNET_strdup (host->h_name);
979 }
980
981
982 /**
983  * Looking our own hostname.
984  *
985  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
986  * @param timeout how long to try resolving
987  * @param callback function to call with addresses
988  * @param cls closure for @a callback
989  * @return handle that can be used to cancel the request, NULL on error
990  */
991 struct GNUNET_RESOLVER_RequestHandle *
992 GNUNET_RESOLVER_hostname_resolve (int af,
993                                   struct GNUNET_TIME_Relative timeout,
994                                   GNUNET_RESOLVER_AddressCallback callback,
995                                   void *cls)
996 {
997   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
998
999   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1000   {
1001     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1002                   "gethostname");
1003     return NULL;
1004   }
1005   LOG (GNUNET_ERROR_TYPE_DEBUG,
1006        "Resolving our hostname `%s'\n",
1007        hostname);
1008   return GNUNET_RESOLVER_ip_get (hostname,
1009                                  af,
1010                                  timeout,
1011                                  callback,
1012                                  cls);
1013 }
1014
1015
1016 /**
1017  * Cancel a request that is still pending with the resolver.
1018  * Note that a client MUST NOT cancel a request that has
1019  * been completed (i.e, the callback has been called to
1020  * signal timeout or the final result).
1021  *
1022  * @param rh handle of request to cancel
1023  */
1024 void
1025 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1026 {
1027   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
1028   {
1029     GNUNET_SCHEDULER_cancel (rh->task);
1030     rh->task = GNUNET_SCHEDULER_NO_TASK;
1031   }
1032   if (GNUNET_NO == rh->was_transmitted)
1033   {
1034     if (rh->was_queued == GNUNET_YES)
1035       GNUNET_CONTAINER_DLL_remove (req_head,
1036                                    req_tail,
1037                                    rh);
1038     GNUNET_free (rh);
1039     return;
1040   }
1041   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1042   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1043 }
1044
1045
1046 /* end of resolver_api.c */