-removing 2nd argument from GNUNET_CLIENT_disconnect as it was virtually always GNUNE...
[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);
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 af address family
276  * @param ip the address
277  * @param ip_len number of bytes in 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, "inet_ntop");
295       return NULL;
296     }
297     break;
298   case AF_INET6:
299     if (ip_len != sizeof (struct in6_addr))
300       return NULL;
301     if (NULL ==
302         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
303     {
304       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
305       return NULL;
306     }
307     break;
308   default:
309     GNUNET_break (0);
310     return NULL;
311   }
312   return GNUNET_strdup (buf);
313 }
314
315
316 /**
317  * Adjust exponential back-off and reconnect to the service.
318  */
319 static void
320 reconnect ();
321
322
323 /**
324  * Process pending requests to the resolver.
325  */
326 static void
327 process_requests ();
328
329
330 /**
331  * Process response with a hostname for a DNS lookup.
332  *
333  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
334  * @param msg message with the hostname, NULL on error
335  */
336 static void
337 handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
338 {
339   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
340   uint16_t size;
341
342 #if DEBUG_RESOLVER
343   LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
344 #endif
345   if (msg == NULL)
346   {
347     char buf[INET6_ADDRSTRLEN];
348
349     if (NULL != rh->name_callback)
350       LOG (GNUNET_ERROR_TYPE_INFO,
351            _("Timeout trying to resolve IP address `%s'.\n"),
352            inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
353     else
354       LOG (GNUNET_ERROR_TYPE_INFO,
355            _("Timeout trying to resolve hostname `%s'.\n"),
356            (const char *) &rh[1]);
357     /* check if request was canceled */
358     if (rh->was_transmitted != GNUNET_SYSERR)
359     {
360       if (NULL != rh->name_callback)
361       {
362         /* no reverse lookup was successful, return ip as string */
363         if (rh->received_response == GNUNET_NO)
364           rh->name_callback (rh->cls,
365                              no_resolve (rh->af,
366                                          &rh[1],
367                                          rh->data_len));
368         /* at least one reverse lookup was successful */
369         else
370           rh->name_callback (rh->cls, NULL);
371       }
372       if (NULL != rh->addr_callback)
373         rh->addr_callback (rh->cls, NULL, 0);
374     }
375     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
376     GNUNET_free (rh);
377     GNUNET_CLIENT_disconnect (client);
378     client = NULL;
379     reconnect ();
380     return;
381   }
382   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
383   {
384     GNUNET_break (0);
385     GNUNET_CLIENT_disconnect (client);
386     client = NULL;
387     reconnect ();
388     return;
389   }
390   size = ntohs (msg->size);
391   /* message contains not data, just header */
392   if (size == sizeof (struct GNUNET_MessageHeader))
393   {
394     /* check if request was canceled */
395     if (rh->was_transmitted != GNUNET_SYSERR)
396     {
397       if (NULL != rh->name_callback)
398         rh->name_callback (rh->cls, NULL);
399       if (NULL != rh->addr_callback)
400         rh->addr_callback (rh->cls, NULL, 0);
401     }
402     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
403     GNUNET_free (rh);
404     process_requests ();
405     return;
406   }
407   /* return reverse lookup results to caller */
408   if (NULL != rh->name_callback)
409   {
410     const char *hostname;
411
412     hostname = (const char *) &msg[1];
413     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
414     {
415       GNUNET_break (0);
416       if (rh->was_transmitted != GNUNET_SYSERR)
417         rh->name_callback (rh->cls, NULL);
418       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
419       GNUNET_free (rh);
420       GNUNET_CLIENT_disconnect (client);
421       client = NULL;
422       reconnect ();
423       return;
424     }
425 #if DEBUG_RESOLVER
426     LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s' for IP `%s'.\n"),
427          hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
428 #endif
429     if (rh->was_transmitted != GNUNET_SYSERR)
430       rh->name_callback (rh->cls, hostname);
431     rh->received_response = GNUNET_YES;
432     GNUNET_CLIENT_receive (client, &handle_response, rh,
433                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
434   }
435   /* return lookup results to caller */
436   if (NULL != rh->addr_callback)
437   {
438     struct sockaddr_in v4;
439     struct sockaddr_in6 v6;
440     const struct sockaddr *sa;
441     socklen_t salen;
442     const void *ip;
443     size_t ip_len;
444
445     ip = &msg[1];
446     ip_len = size - sizeof (struct GNUNET_MessageHeader);
447     if (ip_len == sizeof (struct in_addr))
448     {
449       memset (&v4, 0, sizeof (v4));
450       v4.sin_family = AF_INET;
451       v4.sin_addr = *(struct in_addr*) ip;
452 #if HAVE_SOCKADDR_IN_SIN_LEN
453       v4.sin_len = sizeof (v4);
454 #endif
455       salen = sizeof (v4);
456       sa = (const struct sockaddr *) &v4;
457     }
458     else if (ip_len == sizeof (struct in6_addr))
459     {
460       memset (&v6, 0, sizeof (v6));
461       v6.sin6_family = AF_INET6;
462       v6.sin6_addr = *(struct in6_addr*) ip;
463 #if HAVE_SOCKADDR_IN_SIN_LEN
464       v6.sin6_len = sizeof (v6);
465 #endif
466       salen = sizeof (v6);
467       sa = (const struct sockaddr *) &v6;
468     }
469     else
470     {
471       GNUNET_break (0);
472       if (rh->was_transmitted != GNUNET_SYSERR)
473         rh->addr_callback (rh->cls, NULL, 0);
474       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
475       GNUNET_free (rh);
476       GNUNET_CLIENT_disconnect (client);
477       client = NULL;
478       reconnect ();
479       return;
480     }
481     rh->addr_callback (rh->cls, sa, salen);
482     GNUNET_CLIENT_receive (client, &handle_response, rh,
483                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
484   }
485 }
486
487
488 /**
489  * We've been asked to lookup the address for a hostname and were
490  * given a valid numeric string.  Perform the callbacks for the
491  * numeric addresses.
492  *
493  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
494  * @param tc unused scheduler context
495  */
496 static void
497 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
498 {
499   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
500   struct sockaddr_in v4;
501   struct sockaddr_in6 v6;
502   const char *hostname;
503
504   memset (&v4, 0, sizeof (v4));
505   v4.sin_family = AF_INET;
506 #if HAVE_SOCKADDR_IN_SIN_LEN
507   v4.sin_len = sizeof (v4);
508 #endif
509   memset (&v6, 0, sizeof (v6));
510   v6.sin6_family = AF_INET6;
511 #if HAVE_SOCKADDR_IN_SIN_LEN
512   v6.sin6_len = sizeof (v6);
513 #endif
514   hostname = (const char *) &rh[1];
515   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
516       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
517   {
518     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
519     if ((rh->af == AF_UNSPEC) &&
520         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
521     {
522       /* this can happen on some systems IF "hostname" is "localhost" */
523       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
524     }
525     rh->addr_callback (rh->cls, NULL, 0);
526     GNUNET_free (rh);
527     return;
528   }
529   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
530       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
531   {
532     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
533     rh->addr_callback (rh->cls, NULL, 0);
534     GNUNET_free (rh);
535     return;
536   }
537   /* why are we here? this task should not have been scheduled! */
538   GNUNET_assert (0);
539   GNUNET_free (rh);
540 }
541
542
543 /**
544  * We've been asked to lookup the address for a hostname and were
545  * given a variant of "loopback".  Perform the callbacks for the
546  * respective loopback numeric addresses.
547  *
548  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
549  * @param tc unused scheduler context
550  */
551 static void
552 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
553 {
554   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
555   struct sockaddr_in v4;
556   struct sockaddr_in6 v6;
557
558   memset (&v4, 0, sizeof (v4));
559   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
560   v4.sin_family = AF_INET;
561 #if HAVE_SOCKADDR_IN_SIN_LEN
562   v4.sin_len = sizeof (v4);
563 #endif
564   memset (&v6, 0, sizeof (v6));
565   v6.sin6_family = AF_INET6;
566 #if HAVE_SOCKADDR_IN_SIN_LEN
567   v6.sin6_len = sizeof (v6);
568 #endif
569   v6.sin6_addr = in6addr_loopback;
570   switch (rh->af)
571   {
572   case AF_INET:
573     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
574     break;
575   case AF_INET6:
576     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
577     break;
578   case AF_UNSPEC:
579     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
580     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
581     break;
582   default:
583     GNUNET_break (0);
584     break;
585   }
586   rh->addr_callback (rh->cls, NULL, 0);
587   GNUNET_free (rh);
588 }
589
590
591 /**
592  * Task executed on system shutdown.
593  */
594 static void
595 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
596 {
597   s_task = GNUNET_SCHEDULER_NO_TASK;
598   GNUNET_RESOLVER_disconnect ();
599 }
600
601
602 /**
603  * Process pending requests to the resolver.
604  */
605 static void
606 process_requests ()
607 {
608   struct GNUNET_RESOLVER_GetMessage *msg;
609   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
610   struct GNUNET_RESOLVER_RequestHandle *rh;
611
612   if (NULL == client)
613   {
614     reconnect ();
615     return;
616   }
617   rh = req_head;
618   if (NULL == rh)
619   {
620     /* nothing to do, release socket really soon if there is nothing
621      * else happening... */
622     s_task =
623         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
624                                       &shutdown_task, NULL);
625     return;
626   }
627   if (GNUNET_YES == rh->was_transmitted)
628     return;                     /* waiting for reply */
629   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
630   msg->header.size =
631       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
632   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
633   msg->direction = htonl (rh->direction);
634   msg->af = htonl (rh->af);
635   memcpy (&msg[1], &rh[1], rh->data_len);
636 #if DEBUG_RESOLVER
637   LOG (GNUNET_ERROR_TYPE_DEBUG,
638        "Transmitting DNS resolution request to DNS service\n");
639 #endif
640   if (GNUNET_OK !=
641       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
642                                                GNUNET_TIME_absolute_get_remaining
643                                                (rh->timeout), GNUNET_YES,
644                                                &handle_response, rh))
645   {
646     GNUNET_CLIENT_disconnect (client);
647     client = NULL;
648     reconnect ();
649     return;
650   }
651   rh->was_transmitted = GNUNET_YES;
652 }
653
654
655 /**
656  * Now try to reconnect to the resolver service.
657  *
658  * @param cls NULL
659  * @param tc scheduler context
660  */
661 static void
662 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
663 {
664   r_task = GNUNET_SCHEDULER_NO_TASK;
665   if (NULL == req_head)
666     return;                     /* no work pending */
667   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
668     return;
669 #if DEBUG_RESOLVER
670   LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
671 #endif
672   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
673   if (NULL == client)
674   {
675     LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect, will try again later\n");
676     reconnect ();
677     return;
678   }
679   process_requests ();
680 }
681
682
683 /**
684  * Adjust exponential back-off and reconnect to the service.
685  */
686 static void
687 reconnect ()
688 {
689   struct GNUNET_RESOLVER_RequestHandle *rh;
690
691   if (GNUNET_SCHEDULER_NO_TASK != r_task)
692     return;
693   GNUNET_assert (NULL == client);
694   if (NULL != (rh = req_head))
695   {
696     switch (rh->was_transmitted)
697     {
698     case GNUNET_NO:
699       /* nothing more to do */
700       break;
701     case GNUNET_YES:
702       /* disconnected, transmit again! */
703       rh->was_transmitted = GNUNET_NO;
704       break;
705     case GNUNET_SYSERR:
706       /* request was cancelled, remove entirely */
707       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
708       GNUNET_free (rh);
709       break;
710     default:
711       GNUNET_assert (0);
712       break;
713     }
714   }
715 #if DEBUG_RESOLVER
716   LOG (GNUNET_ERROR_TYPE_DEBUG,
717        "Will try to connect to DNS service in %llu ms\n",
718        (unsigned long long) backoff.rel_value);
719 #endif
720   GNUNET_assert (NULL != resolver_cfg);
721   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
722   backoff = GNUNET_TIME_relative_multiply (backoff, 2);
723 }
724
725
726 /**
727  * Convert a string to one or more IP addresses.
728  *
729  * @param hostname the hostname to resolve
730  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
731  * @param callback function to call with addresses
732  * @param callback_cls closure for callback
733  * @param timeout how long to try resolving
734  * @return handle that can be used to cancel the request, NULL on error
735  */
736 struct GNUNET_RESOLVER_RequestHandle *
737 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
738                         struct GNUNET_TIME_Relative timeout,
739                         GNUNET_RESOLVER_AddressCallback callback,
740                         void *callback_cls)
741 {
742   struct GNUNET_RESOLVER_RequestHandle *rh;
743   size_t slen;
744   unsigned int i;
745   struct in_addr v4;
746   struct in6_addr v6;
747
748   slen = strlen (hostname) + 1;
749   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
750       GNUNET_SERVER_MAX_MESSAGE_SIZE)
751   {
752     GNUNET_break (0);
753     return NULL;
754   }
755   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
756   rh->af = af;
757   rh->addr_callback = callback;
758   rh->cls = callback_cls;
759   memcpy (&rh[1], hostname, slen);
760   rh->data_len = slen;
761   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
762   rh->direction = GNUNET_NO;
763   /* first, check if this is a numeric address */
764   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
765        ((af == AF_INET) || (af == AF_UNSPEC))) ||
766       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
767        ((af == AF_INET6) || (af == AF_UNSPEC))))
768   {
769     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
770     return rh;
771   }
772   /* then, check if this is a loopback address */
773   i = 0;
774   while (loopback[i] != NULL)
775     if (0 == strcasecmp (loopback[i++], hostname))
776     {
777       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
778       return rh;
779     }
780   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
781   rh->was_queued = GNUNET_YES;
782   if (s_task != GNUNET_SCHEDULER_NO_TASK)
783   {
784     GNUNET_SCHEDULER_cancel (s_task);
785     s_task = GNUNET_SCHEDULER_NO_TASK;
786   }
787   process_requests ();
788   return rh;
789 }
790
791
792 /**
793  * We've been asked to convert an address to a string without
794  * a reverse lookup.  Do it.
795  *
796  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
797  * @param tc unused scheduler context
798  */
799 static void
800 numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
801 {
802   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
803   char *result;
804
805   result = no_resolve (rh->af, &rh[1], rh->data_len);
806 #if DEBUG_RESOLVER
807   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
808 #endif
809   if (result != NULL)
810   {
811     rh->name_callback (rh->cls, result);
812     GNUNET_free (result);
813   }
814   rh->name_callback (rh->cls, NULL);
815   GNUNET_free (rh);
816 }
817
818
819 /**
820  * Get an IP address as a string.
821  *
822  * @param sa host address
823  * @param salen length of host address
824  * @param do_resolve use GNUNET_NO to return numeric hostname
825  * @param timeout how long to try resolving
826  * @param callback function to call with hostnames
827  *        last callback is NULL when finished
828  * @param cls closure for callback
829  * @return handle that can be used to cancel the request
830  */
831 struct GNUNET_RESOLVER_RequestHandle *
832 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
833                               int do_resolve,
834                               struct GNUNET_TIME_Relative timeout,
835                               GNUNET_RESOLVER_HostnameCallback callback,
836                               void *cls)
837 {
838   struct GNUNET_RESOLVER_RequestHandle *rh;
839   size_t ip_len;
840   const void *ip;
841
842   check_config ();
843   switch (sa->sa_family)
844   {
845   case AF_INET:
846     ip_len = sizeof (struct in_addr);
847     ip = &((const struct sockaddr_in*)sa)->sin_addr;
848     break;
849   case AF_INET6:
850     ip_len = sizeof (struct in6_addr);
851     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
852     break;
853   default:
854     GNUNET_break (0);
855     return NULL;
856   }
857   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
858   rh->name_callback = callback;
859   rh->cls = cls;
860   rh->af = sa->sa_family;
861   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
862   memcpy (&rh[1], ip, ip_len);
863   rh->data_len = ip_len;
864   rh->direction = GNUNET_YES;
865   rh->received_response = GNUNET_NO;
866   if (GNUNET_NO == do_resolve)
867   {
868     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
869     return rh;
870   }
871   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
872   rh->was_queued = GNUNET_YES;
873   if (s_task != GNUNET_SCHEDULER_NO_TASK)
874   {
875     GNUNET_SCHEDULER_cancel (s_task);
876     s_task = GNUNET_SCHEDULER_NO_TASK;
877   }
878   process_requests ();
879   return rh;
880 }
881
882
883 /**
884  * Get local fully qualified af name
885  *
886  * @return fqdn
887  */
888 char *
889 GNUNET_RESOLVER_local_fqdn_get ()
890 {
891   struct hostent *host;
892   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
893
894   if (0 != gethostname (hostname, sizeof (hostname) - 1))
895   {
896     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
897                   "gethostname");
898     return NULL;
899   }
900 #if DEBUG_RESOLVER
901   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our FQDN `%s'\n"), hostname);
902 #endif
903   host = gethostbyname (hostname);
904   if (NULL == host)
905   {
906     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
907          hstrerror (h_errno));
908     return NULL;
909   }
910   return GNUNET_strdup (host->h_name);
911 }
912
913
914 /**
915  * Looking our own hostname.
916  *
917  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
918  * @param callback function to call with addresses
919  * @param cls closure for callback
920  * @param timeout how long to try resolving
921  * @return handle that can be used to cancel the request, NULL on error
922  */
923 struct GNUNET_RESOLVER_RequestHandle *
924 GNUNET_RESOLVER_hostname_resolve (int af,
925                                   struct GNUNET_TIME_Relative timeout,
926                                   GNUNET_RESOLVER_AddressCallback callback,
927                                   void *cls)
928 {
929   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
930
931   if (0 != gethostname (hostname, sizeof (hostname) - 1))
932   {
933     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
934                   "gethostname");
935     return NULL;
936   }
937 #if DEBUG_RESOLVER
938   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our hostname `%s'\n"), hostname);
939 #endif
940   return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
941 }
942
943
944 /**
945  * Cancel a request that is still pending with the resolver.
946  * Note that a client MUST NOT cancel a request that has
947  * been completed (i.e, the callback has been called to
948  * signal timeout or the final result).
949  *
950  * @param rh handle of request to cancel
951  */
952 void
953 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
954 {
955   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
956   {
957     GNUNET_SCHEDULER_cancel (rh->task);
958     rh->task = GNUNET_SCHEDULER_NO_TASK;
959   }
960   if (rh->was_transmitted == GNUNET_NO)
961   {
962     if (rh->was_queued == GNUNET_YES)
963       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
964     GNUNET_free (rh);
965     return;
966   }
967   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
968   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
969 }
970
971
972 /* end of resolver_api.c */