-fix
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2011 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 2, 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_getopt_lib.h"
28 #include "gnunet_os_lib.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_container_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_server_lib.h"
34 #include "resolver.h"
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
37
38 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
39
40 /**
41  * Maximum supported length for a hostname
42  */
43 #define MAX_HOSTNAME 1024
44
45
46 /**
47  * Possible hostnames for "loopback".
48  */
49 static const char *loopback[] = {
50   "localhost",
51   "ip6-localnet",
52   NULL
53 };
54
55
56 /**
57  * Configuration.
58  */
59 static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
60
61 /**
62  * Our connection to the resolver service, created on-demand, but then
63  * persists until error or shutdown.
64  */
65 static struct GNUNET_CLIENT_Connection *client;
66
67 /**
68  * Head of DLL of requests.
69  */
70 static struct GNUNET_RESOLVER_RequestHandle *req_head;
71
72 /**
73  * Tail of DLL of requests.
74  */
75 static struct GNUNET_RESOLVER_RequestHandle *req_tail;
76
77 /**
78  * How long should we wait to reconnect?
79  */
80 static struct GNUNET_TIME_Relative backoff;
81
82 /**
83  * Task for reconnecting.
84  */
85 static GNUNET_SCHEDULER_TaskIdentifier r_task;
86
87 /**
88  * Task ID of shutdown task; only present while we have a
89  * connection to the resolver service.
90  */
91 static GNUNET_SCHEDULER_TaskIdentifier s_task;
92
93
94 /**
95  * Handle to a request given to the resolver.  Can be used to cancel
96  * the request prior to the timeout or successful execution.  Also
97  * used to track our internal state for the request.
98  */
99 struct GNUNET_RESOLVER_RequestHandle
100 {
101
102   /**
103    * Next entry in DLL of requests.
104    */
105   struct GNUNET_RESOLVER_RequestHandle *next;
106
107   /**
108    * Previous entry in DLL of requests.
109    */
110   struct GNUNET_RESOLVER_RequestHandle *prev;
111
112   /**
113    * Callback if this is an name resolution request,
114    * otherwise NULL.
115    */
116   GNUNET_RESOLVER_AddressCallback addr_callback;
117
118   /**
119    * Callback if this is a reverse lookup request,
120    * otherwise NULL.
121    */
122   GNUNET_RESOLVER_HostnameCallback name_callback;
123
124   /**
125    * Closure for the respective "callback".
126    */
127   void *cls;
128
129   /**
130    * When should this request time out?
131    */
132   struct GNUNET_TIME_Absolute timeout;
133
134   /**
135    * Task handle for numeric lookups.
136    */
137   GNUNET_SCHEDULER_TaskIdentifier task;
138
139   /**
140    * Desired address family.
141    */
142   int af;
143
144   /**
145    * Has this request been transmitted to the service?
146    * GNUNET_YES if transmitted
147    * GNUNET_YES if not transmitted
148    * GNUNET_SYSERR when request was canceled
149    */
150   int was_transmitted;
151
152   /**
153    * Did we add this request to the queue?
154    */
155   int was_queued;
156
157   /**
158    * Desired direction (IP to name or name to IP)
159    */
160   int direction;
161
162   /**
163    * GNUNET_YES if a response was received
164    */
165   int received_response;
166
167   /**
168    * Length of the data that follows this struct.
169    */
170   size_t data_len;
171 };
172
173
174 /**
175  * Check that the resolver service runs on localhost
176  * (or equivalent).
177  */
178 static void
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, "resolver",
199                                              "HOSTNAME", &hostname))
200   {
201     LOG (GNUNET_ERROR_TYPE_ERROR,
202          _("Must specify `%s' for `%s' in configuration!\n"), "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 (loopback[i] != NULL)
214     if (0 == strcasecmp (loopback[i++], hostname))
215     {
216       GNUNET_free (hostname);
217       return;
218     }
219   LOG (GNUNET_ERROR_TYPE_ERROR,
220        _
221        ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
222        "localhost", "HOSTNAME", "resolver");
223   GNUNET_free (hostname);
224   GNUNET_assert (0);
225 }
226
227
228 /**
229  * Create the connection to the resolver service.
230  *
231  * @param cfg configuration to use
232  */
233 void
234 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
235 {
236   GNUNET_assert (NULL != cfg);
237   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
238   resolver_cfg = cfg;
239   check_config ();
240 }
241
242
243 /**
244  * Destroy the connection to the resolver service.
245  */
246 void
247 GNUNET_RESOLVER_disconnect ()
248 {
249   GNUNET_assert (NULL == req_head);
250   GNUNET_assert (NULL == req_tail);
251   if (NULL != client)
252   {
253 #if DEBUG_RESOLVER
254     LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
255 #endif
256     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
257     client = NULL;
258   }
259   if (r_task != GNUNET_SCHEDULER_NO_TASK)
260   {
261     GNUNET_SCHEDULER_cancel (r_task);
262     r_task = GNUNET_SCHEDULER_NO_TASK;
263   }
264   if (s_task != GNUNET_SCHEDULER_NO_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 sa the address
276  * @param salen number of bytes in sa
277  * @return address as a string, NULL on error
278  */
279 static char *
280 no_resolve (int af,
281             const void *ip, socklen_t ip_len)
282 {
283   char buf[INET6_ADDRSTRLEN];
284
285   switch (af)
286   {
287   case AF_INET:
288     if (ip_len != sizeof (struct in_addr))
289       return NULL;
290     if (NULL ==
291         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
292     {
293       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
294       return NULL;
295     }
296     break;
297   case AF_INET6:
298     if (ip_len != sizeof (struct in6_addr))
299       return NULL;
300     if (NULL ==
301         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
302     {
303       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
304       return NULL;
305     }
306     break;
307   default:
308     GNUNET_break (0);
309     return NULL;
310   }
311   return GNUNET_strdup (buf);
312 }
313
314
315 /**
316  * Adjust exponential back-off and reconnect to the service.
317  */
318 static void
319 reconnect ();
320
321
322 /**
323  * Process pending requests to the resolver.
324  */
325 static void
326 process_requests ();
327
328
329 /**
330  * Process response with a hostname for a DNS lookup.
331  *
332  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
333  * @param msg message with the hostname, NULL on error
334  */
335 static void
336 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
337 {
338   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
339   uint16_t size;
340
341 #if DEBUG_RESOLVER
342   LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
343 #endif
344   if (msg == NULL)
345   {
346     char buf[INET6_ADDRSTRLEN];
347
348     if (NULL != rh->name_callback)
349       LOG (GNUNET_ERROR_TYPE_INFO,
350            _("Timeout trying to resolve IP address `%s'.\n"),
351            inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
352     else
353       LOG (GNUNET_ERROR_TYPE_INFO,
354            _("Timeout trying to resolve hostname `%s'.\n"),
355            (const char *) &rh[1]);
356     /* check if request was canceled */
357     if (rh->was_transmitted != GNUNET_SYSERR)
358     {
359       if (NULL != rh->name_callback)
360       {
361         /* no reverse lookup was successful, return ip as string */
362         if (rh->received_response == GNUNET_NO)
363           rh->name_callback (rh->cls,
364                              no_resolve (rh->af,
365                                          &rh[1],
366                                          rh->data_len));
367         /* at least one reverse lookup was successful */
368         else
369           rh->name_callback (rh->cls, NULL);
370       }
371       if (NULL != rh->addr_callback)
372         rh->addr_callback (rh->cls, NULL, 0);
373     }
374     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
375     GNUNET_free (rh);
376     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
377     client = NULL;
378     reconnect ();
379     return;
380   }
381   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
382   {
383     GNUNET_break (0);
384     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
385     client = NULL;
386     reconnect ();
387     return;
388   }
389   size = ntohs (msg->size);
390   /* message contains not data, just header */
391   if (size == sizeof (struct GNUNET_MessageHeader))
392   {
393     /* check if request was canceled */
394     if (rh->was_transmitted != GNUNET_SYSERR)
395     {
396       if (NULL != rh->name_callback)
397         rh->name_callback (rh->cls, NULL);
398       if (NULL != rh->addr_callback)
399         rh->addr_callback (rh->cls, NULL, 0);
400     }
401     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
402     GNUNET_free (rh);
403     process_requests ();
404     return;
405   }
406   /* return reverse lookup results to caller */
407   if (NULL != rh->name_callback)
408   {
409     const char *hostname;
410
411     hostname = (const char *) &msg[1];
412     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
413     {
414       GNUNET_break (0);
415       if (rh->was_transmitted != GNUNET_SYSERR)
416         rh->name_callback (rh->cls, NULL);
417       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
418       GNUNET_free (rh);
419       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
420       client = NULL;
421       reconnect ();
422       return;
423     }
424 #if DEBUG_RESOLVER
425     LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s' for IP `%s'.\n"),
426          hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
427 #endif
428     if (rh->was_transmitted != GNUNET_SYSERR)
429       rh->name_callback (rh->cls, hostname);
430     rh->received_response = GNUNET_YES;
431     GNUNET_CLIENT_receive (client, &handle_response, rh,
432                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
433   }
434   /* return lookup results to caller */
435   if (NULL != rh->addr_callback)
436   {
437     struct sockaddr_in v4;
438     struct sockaddr_in6 v6;
439     const struct sockaddr *sa;
440     socklen_t salen;
441     const void *ip;
442     size_t ip_len;
443
444     ip = &msg[1];
445     ip_len = size - sizeof (struct GNUNET_MessageHeader);
446     if (ip_len == sizeof (struct in_addr))
447     {
448       memset (&v4, 0, sizeof (v4));
449       v4.sin_family = AF_INET;
450       v4.sin_addr = *(struct in_addr*) ip;
451 #if HAVE_SOCKADDR_IN_SIN_LEN
452       v4.sin_len = sizeof (v4);
453 #endif
454       salen = sizeof (v4);
455       sa = (const struct sockaddr *) &v4;
456     }
457     else if (ip_len == sizeof (struct in6_addr))
458     {
459       memset (&v6, 0, sizeof (v6));
460       v6.sin6_family = AF_INET6;
461       v6.sin6_addr = *(struct in6_addr*) ip;
462 #if HAVE_SOCKADDR_IN_SIN_LEN
463       v6.sin6_len = sizeof (v6);
464 #endif
465       salen = sizeof (v6);
466       sa = (const struct sockaddr *) &v6;
467     }
468     else
469     {
470       GNUNET_break (0);
471       if (rh->was_transmitted != GNUNET_SYSERR)
472         rh->addr_callback (rh->cls, NULL, 0);
473       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
474       GNUNET_free (rh);
475       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
476       client = NULL;
477       reconnect ();
478       return;
479     }
480     rh->addr_callback (rh->cls, sa, salen);
481     GNUNET_CLIENT_receive (client, &handle_response, rh,
482                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
483   }
484 }
485
486
487 /**
488  * We've been asked to lookup the address for a hostname and were
489  * given a valid numeric string.  Perform the callbacks for the
490  * numeric addresses.
491  *
492  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
493  * @param tc unused scheduler context
494  */
495 static void
496 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
497 {
498   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
499   struct sockaddr_in v4;
500   struct sockaddr_in6 v6;
501   const char *hostname;
502
503   memset (&v4, 0, sizeof (v4));
504   v4.sin_family = AF_INET;
505 #if HAVE_SOCKADDR_IN_SIN_LEN
506   v4.sin_len = sizeof (v4);
507 #endif
508   memset (&v6, 0, sizeof (v6));
509   v6.sin6_family = AF_INET6;
510 #if HAVE_SOCKADDR_IN_SIN_LEN
511   v6.sin6_len = sizeof (v6);
512 #endif
513   hostname = (const char *) &rh[1];
514   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
515       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
516   {
517     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
518     if ((rh->af == AF_UNSPEC) &&
519         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
520     {
521       /* this can happen on some systems IF "hostname" is "localhost" */
522       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
523     }
524     rh->addr_callback (rh->cls, NULL, 0);
525     GNUNET_free (rh);
526     return;
527   }
528   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
529       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
530   {
531     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
532     rh->addr_callback (rh->cls, NULL, 0);
533     GNUNET_free (rh);
534     return;
535   }
536   /* why are we here? this task should not have been scheduled! */
537   GNUNET_assert (0);
538   GNUNET_free (rh);
539 }
540
541
542 /**
543  * We've been asked to lookup the address for a hostname and were
544  * given a variant of "loopback".  Perform the callbacks for the
545  * respective loopback numeric addresses.
546  *
547  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
548  * @param tc unused scheduler context
549  */
550 static void
551 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
552 {
553   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
554   struct sockaddr_in v4;
555   struct sockaddr_in6 v6;
556
557   memset (&v4, 0, sizeof (v4));
558   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
559   v4.sin_family = AF_INET;
560 #if HAVE_SOCKADDR_IN_SIN_LEN
561   v4.sin_len = sizeof (v4);
562 #endif
563   memset (&v6, 0, sizeof (v6));
564   v6.sin6_family = AF_INET6;
565 #if HAVE_SOCKADDR_IN_SIN_LEN
566   v6.sin6_len = sizeof (v6);
567 #endif
568   v6.sin6_addr = in6addr_loopback;
569   switch (rh->af)
570   {
571   case AF_INET:
572     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
573     break;
574   case AF_INET6:
575     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
576     break;
577   case AF_UNSPEC:
578     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
579     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
580     break;
581   default:
582     GNUNET_break (0);
583     break;
584   }
585   rh->addr_callback (rh->cls, NULL, 0);
586   GNUNET_free (rh);
587 }
588
589
590 /**
591  * Task executed on system shutdown.
592  */
593 static void
594 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
595 {
596   s_task = GNUNET_SCHEDULER_NO_TASK;
597   GNUNET_RESOLVER_disconnect ();
598 }
599
600
601 /**
602  * Process pending requests to the resolver.
603  */
604 static void
605 process_requests ()
606 {
607   struct GNUNET_RESOLVER_GetMessage *msg;
608   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
609   struct GNUNET_RESOLVER_RequestHandle *rh;
610
611   if (NULL == client)
612   {
613     reconnect ();
614     return;
615   }
616   rh = req_head;
617   if (NULL == rh)
618   {
619     /* nothing to do, release socket really soon if there is nothing
620      * else happening... */
621     s_task =
622         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
623                                       &shutdown_task, NULL);
624     return;
625   }
626   if (GNUNET_YES == rh->was_transmitted)
627     return;                     /* waiting for reply */
628   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
629   msg->header.size =
630       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
631   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
632   msg->direction = htonl (rh->direction);
633   msg->af = htonl (rh->af);
634   memcpy (&msg[1], &rh[1], rh->data_len);
635 #if DEBUG_RESOLVER
636   LOG (GNUNET_ERROR_TYPE_DEBUG,
637        "Transmitting DNS resolution request to DNS service\n");
638 #endif
639   if (GNUNET_OK !=
640       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
641                                                GNUNET_TIME_absolute_get_remaining
642                                                (rh->timeout), GNUNET_YES,
643                                                &handle_response, rh))
644   {
645     GNUNET_CLIENT_disconnect (client, GNUNET_NO);
646     client = NULL;
647     reconnect ();
648     return;
649   }
650   rh->was_transmitted = GNUNET_YES;
651 }
652
653
654 /**
655  * Now try to reconnect to the resolver service.
656  *
657  * @param cls NULL
658  * @param tc scheduler context
659  */
660 static void
661 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
662 {
663   r_task = GNUNET_SCHEDULER_NO_TASK;
664   if (NULL == req_head)
665     return;                     /* no work pending */
666   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
667     return;
668 #if DEBUG_RESOLVER
669   LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
670 #endif
671   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
672   if (NULL == client)
673   {
674     LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect, will try again later\n");
675     reconnect ();
676     return;
677   }
678   process_requests ();
679 }
680
681
682 /**
683  * Adjust exponential back-off and reconnect to the service.
684  */
685 static void
686 reconnect ()
687 {
688   struct GNUNET_RESOLVER_RequestHandle *rh;
689
690   if (GNUNET_SCHEDULER_NO_TASK != r_task)
691     return;
692   GNUNET_assert (NULL == client);
693   if (NULL != (rh = req_head))
694   {
695     switch (rh->was_transmitted)
696     {
697     case GNUNET_NO:
698       /* nothing more to do */
699       break;
700     case GNUNET_YES:
701       /* disconnected, transmit again! */
702       rh->was_transmitted = GNUNET_NO;
703       break;
704     case GNUNET_SYSERR:
705       /* request was cancelled, remove entirely */
706       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
707       GNUNET_free (rh);
708       break;
709     default:
710       GNUNET_assert (0);
711       break;
712     }
713   }
714 #if DEBUG_RESOLVER
715   LOG (GNUNET_ERROR_TYPE_DEBUG,
716        "Will try to connect to DNS service in %llu ms\n",
717        (unsigned long long) backoff.rel_value);
718 #endif
719   GNUNET_assert (NULL != resolver_cfg);
720   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
721   backoff = GNUNET_TIME_relative_multiply (backoff, 2);
722 }
723
724
725 /**
726  * Convert a string to one or more IP addresses.
727  *
728  * @param hostname the hostname to resolve
729  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
730  * @param callback function to call with addresses
731  * @param callback_cls closure for callback
732  * @param timeout how long to try resolving
733  * @return handle that can be used to cancel the request, NULL on error
734  */
735 struct GNUNET_RESOLVER_RequestHandle *
736 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
737                         struct GNUNET_TIME_Relative timeout,
738                         GNUNET_RESOLVER_AddressCallback callback,
739                         void *callback_cls)
740 {
741   struct GNUNET_RESOLVER_RequestHandle *rh;
742   size_t slen;
743   unsigned int i;
744   struct in_addr v4;
745   struct in6_addr v6;
746
747   slen = strlen (hostname) + 1;
748   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
749       GNUNET_SERVER_MAX_MESSAGE_SIZE)
750   {
751     GNUNET_break (0);
752     return NULL;
753   }
754   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
755   rh->af = af;
756   rh->addr_callback = callback;
757   rh->cls = callback_cls;
758   memcpy (&rh[1], hostname, slen);
759   rh->data_len = slen;
760   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
761   rh->direction = GNUNET_NO;
762   /* first, check if this is a numeric address */
763   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
764        ((af == AF_INET) || (af == AF_UNSPEC))) ||
765       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
766        ((af == AF_INET6) || (af == AF_UNSPEC))))
767   {
768     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
769     return rh;
770   }
771   /* then, check if this is a loopback address */
772   i = 0;
773   while (loopback[i] != NULL)
774     if (0 == strcasecmp (loopback[i++], hostname))
775     {
776       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
777       return rh;
778     }
779   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
780   rh->was_queued = GNUNET_YES;
781   if (s_task != GNUNET_SCHEDULER_NO_TASK)
782   {
783     GNUNET_SCHEDULER_cancel (s_task);
784     s_task = GNUNET_SCHEDULER_NO_TASK;
785   }
786   process_requests ();
787   return rh;
788 }
789
790
791 /**
792  * We've been asked to convert an address to a string without
793  * a reverse lookup.  Do it.
794  *
795  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
796  * @param tc unused scheduler context
797  */
798 static void
799 numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
800 {
801   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
802   char *result;
803
804   result = no_resolve (rh->af, &rh[1], rh->data_len);
805 #if DEBUG_RESOLVER
806   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
807 #endif
808   if (result != NULL)
809   {
810     rh->name_callback (rh->cls, result);
811     GNUNET_free (result);
812   }
813   rh->name_callback (rh->cls, NULL);
814   GNUNET_free (rh);
815 }
816
817
818 /**
819  * Get an IP address as a string.
820  *
821  * @param sa host address
822  * @param salen length of host address
823  * @param do_resolve use GNUNET_NO to return numeric hostname
824  * @param timeout how long to try resolving
825  * @param callback function to call with hostnames
826  *        last callback is NULL when finished
827  * @param cls closure for callback
828  * @return handle that can be used to cancel the request
829  */
830 struct GNUNET_RESOLVER_RequestHandle *
831 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
832                               int do_resolve,
833                               struct GNUNET_TIME_Relative timeout,
834                               GNUNET_RESOLVER_HostnameCallback callback,
835                               void *cls)
836 {
837   struct GNUNET_RESOLVER_RequestHandle *rh;
838   size_t ip_len;
839   const void *ip;
840
841   check_config ();
842   switch (sa->sa_family)
843   {
844   case AF_INET:
845     ip_len = sizeof (struct in_addr);
846     ip = &((const struct sockaddr_in*)sa)->sin_addr;
847     break;
848   case AF_INET6:
849     ip_len = sizeof (struct in6_addr);
850     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
851     break;
852   default:
853     GNUNET_break (0);
854     return NULL;
855   }
856   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
857   rh->name_callback = callback;
858   rh->cls = cls;
859   rh->af = sa->sa_family;
860   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
861   memcpy (&rh[1], ip, ip_len);
862   rh->data_len = ip_len;
863   rh->direction = GNUNET_YES;
864   rh->received_response = GNUNET_NO;
865   if (GNUNET_NO == do_resolve)
866   {
867     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
868     return rh;
869   }
870   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
871   rh->was_queued = GNUNET_YES;
872   if (s_task != GNUNET_SCHEDULER_NO_TASK)
873   {
874     GNUNET_SCHEDULER_cancel (s_task);
875     s_task = GNUNET_SCHEDULER_NO_TASK;
876   }
877   process_requests ();
878   return rh;
879 }
880
881
882 /**
883  * Get local fully qualified af name
884  *
885  * @return fqdn
886  */
887 char *
888 GNUNET_RESOLVER_local_fqdn_get ()
889 {
890   struct hostent *host;
891   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
892
893   if (0 != gethostname (hostname, sizeof (hostname) - 1))
894   {
895     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
896                   "gethostname");
897     return NULL;
898   }
899 #if DEBUG_RESOLVER
900   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our FQDN `%s'\n"), hostname);
901 #endif
902   host = gethostbyname (hostname);
903   if (NULL == host)
904   {
905     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
906          hstrerror (h_errno));
907     return NULL;
908   }
909   return GNUNET_strdup (host->h_name);
910 }
911
912
913 /**
914  * Looking our own hostname.
915  *
916  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
917  * @param callback function to call with addresses
918  * @param cls closure for callback
919  * @param timeout how long to try resolving
920  * @return handle that can be used to cancel the request, NULL on error
921  */
922 struct GNUNET_RESOLVER_RequestHandle *
923 GNUNET_RESOLVER_hostname_resolve (int af,
924                                   struct GNUNET_TIME_Relative timeout,
925                                   GNUNET_RESOLVER_AddressCallback callback,
926                                   void *cls)
927 {
928   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
929
930   if (0 != gethostname (hostname, sizeof (hostname) - 1))
931   {
932     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
933                   "gethostname");
934     return NULL;
935   }
936 #if DEBUG_RESOLVER
937   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our hostname `%s'\n"), hostname);
938 #endif
939   return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
940 }
941
942
943 /**
944  * Cancel a request that is still pending with the resolver.
945  * Note that a client MUST NOT cancel a request that has
946  * been completed (i.e, the callback has been called to
947  * signal timeout or the final result).
948  *
949  * @param rh handle of request to cancel
950  */
951 void
952 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
953 {
954   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
955   {
956     GNUNET_SCHEDULER_cancel (rh->task);
957     rh->task = GNUNET_SCHEDULER_NO_TASK;
958   }
959   if (rh->was_transmitted == GNUNET_NO)
960   {
961     if (rh->was_queued == GNUNET_YES)
962       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
963     GNUNET_free (rh);
964     return;
965   }
966   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
967   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
968 }
969
970
971 /* end of resolver_api.c */