allowing cancellation of resolver requests
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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_client_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_resolver_service.h"
31 #include "gnunet_server_lib.h"
32 #include "resolver.h"
33
34
35 /**
36  * Maximum supported length for a hostname
37  */
38 #define MAX_HOSTNAME 1024
39
40
41 /**
42  * Possible hostnames for "loopback".
43  */
44 static const char *loopback[] = {
45   "localhost",
46   "ip6-localnet",
47   NULL
48 };
49
50
51 /**
52  * Handle to a request given to the resolver.  Can be used to cancel
53  * the request prior to the timeout or successful execution.  Also
54  * used to track our internal state for the request.
55  */
56 struct GNUNET_RESOLVER_RequestHandle
57 {
58
59   /**
60    * Callback if this is an name resolution request,
61    * otherwise NULL.
62    */
63   GNUNET_RESOLVER_AddressCallback addr_callback;
64
65   /**
66    * Callback if this is a reverse lookup request,
67    * otherwise NULL.
68    */
69   GNUNET_RESOLVER_HostnameCallback name_callback;
70
71   /**
72    * Closure for the respective "callback".
73    */
74   void *cls;
75
76   /**
77    * Our connection to the resolver service.
78    */
79   struct GNUNET_CLIENT_Connection *client;
80
81   /**
82    * Our scheduler.
83    */
84   struct GNUNET_SCHEDULER_Handle *sched;
85
86   /**
87    * Name of the host that we are resolving.
88    */
89   const char *hostname;
90
91   /**
92    * When should this request time out?
93    */
94   struct GNUNET_TIME_Absolute timeout;
95
96   /**
97    * Task handle for numeric lookups.
98    */
99   GNUNET_SCHEDULER_TaskIdentifier task;
100
101   /**
102    * Desired address family.
103    */ 
104   int domain;
105
106   /**
107    * Length of the "struct sockaddr" that follows this
108    * struct (only for reverse lookup).
109    */
110   socklen_t salen;
111 };
112
113
114 /**
115  * Check that the resolver service runs on localhost
116  * (or equivalent).
117  */
118 static void
119 check_config (const struct GNUNET_CONFIGURATION_Handle *cfg)
120 {
121   char *hostname;
122   unsigned int i;
123   struct sockaddr_in v4;
124   struct sockaddr_in6 v6;
125
126   memset (&v4, 0, sizeof(v4));
127   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
128   v4.sin_family = AF_INET;
129 #if HAVE_SOCKADDR_IN_SIN_LEN
130   v4.sin_len = sizeof(v4);
131 #endif
132   memset (&v6, 0, sizeof(v6)); 
133   v6.sin6_family = AF_INET6;
134 #if HAVE_SOCKADDR_IN_SIN_LEN
135   v6.sin6_len = sizeof(v6);
136 #endif
137   if (GNUNET_OK !=
138       GNUNET_CONFIGURATION_get_value_string (cfg,
139                                              "resolver",
140                                              "HOSTNAME",
141                                              &hostname))
142     {
143       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144                   _("Must specify `%s' for `%s' in configuration!\n"),
145                   "HOSTNAME",
146                   "resolver");
147       GNUNET_assert (0);
148     }
149   if ( (1 != inet_pton (AF_INET,
150                         hostname,
151                         &v4)) ||
152        (1 != inet_pton (AF_INET6,
153                         hostname,
154                         &v6)) )
155     {
156       GNUNET_free (hostname);
157       return;
158     }
159   i = 0;
160   while (loopback[i] != NULL)
161     if (0 == strcasecmp (loopback[i++], hostname))
162       {
163         GNUNET_free (hostname); 
164         return;
165       }
166   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167               _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
168               "localhost",
169               "HOSTNAME",
170               "resolver");
171   GNUNET_free (hostname); 
172   GNUNET_assert (0); 
173 }
174
175
176 /**
177  * Convert IP address to string without DNS resolution.
178  *
179  * @param sa the address 
180  * @param salen number of bytes in sa
181  * @return address as a string, NULL on error
182  */
183 static char *
184 no_resolve (const struct sockaddr *sa, socklen_t salen)
185 {
186   char *ret;
187   char inet4[INET_ADDRSTRLEN];
188   char inet6[INET6_ADDRSTRLEN];
189
190   if (salen < sizeof (struct sockaddr))
191     return NULL;
192   switch (sa->sa_family)
193     {
194     case AF_INET:
195       if (salen != sizeof (struct sockaddr_in))
196         return NULL;
197       inet_ntop (AF_INET,
198                  &((struct sockaddr_in *) sa)->sin_addr,
199                  inet4, INET_ADDRSTRLEN);
200       ret = GNUNET_strdup (inet4);
201       break;
202     case AF_INET6:
203       if (salen != sizeof (struct sockaddr_in6))
204         return NULL;
205       inet_ntop (AF_INET6,
206                  &((struct sockaddr_in6 *) sa)->sin6_addr,
207                  inet6, INET6_ADDRSTRLEN);
208       ret = GNUNET_strdup (inet6);
209       break;
210     default:
211       ret = NULL;
212       break;
213     }
214   return ret;
215 }
216
217
218 /**
219  * Process the reply from the resolver (which is presumably
220  * the numeric IP address for a name resolution request).
221  *
222  * @param cls the "GNUNET_RESOLVER_RequestHandle" for which this is a reply
223  * @param msg reply from the resolver or NULL on error
224  */
225 static void
226 handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
227 {
228   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
229   uint16_t size;
230   const struct sockaddr *sa;
231   socklen_t salen;
232
233
234   if (msg == NULL)
235     {
236       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
237                   _("Timeout trying to resolve hostname.\n"));
238       rh->addr_callback (rh->cls, NULL, 0);
239       GNUNET_CLIENT_disconnect (rh->client);
240       GNUNET_free (rh);
241       return;
242     }
243   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
244     {
245       GNUNET_break (0);
246       rh->addr_callback (rh->cls, NULL, 0);
247       GNUNET_CLIENT_disconnect (rh->client);
248       GNUNET_free (rh);
249       return;
250     }
251
252   size = ntohs (msg->size);
253   if (size == sizeof (struct GNUNET_MessageHeader))
254     {
255 #if DEBUG_RESOLVER
256       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257                   _("Received end message resolving hostname.\n"));
258 #endif
259       rh->addr_callback (rh->cls, NULL, 0);
260       GNUNET_CLIENT_disconnect (rh->client);
261       GNUNET_free (rh);
262       return;
263     }
264   sa = (const struct sockaddr *) &msg[1];
265   salen = size - sizeof (struct GNUNET_MessageHeader);
266   if (salen < sizeof (struct sockaddr))
267     {
268       GNUNET_break (0);
269       rh->addr_callback (rh->cls, NULL, 0);
270       GNUNET_CLIENT_disconnect (rh->client);
271       GNUNET_free (rh);
272       return;
273     }
274 #if DEBUG_RESOLVER
275   {
276     char *ips = no_resolve (sa, salen);
277     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
278                 "Resolver returns `%s'.\n", 
279                 ips);
280     GNUNET_free (ips);
281   }
282 #endif
283   rh->addr_callback (rh->cls, sa, salen);
284   GNUNET_CLIENT_receive (rh->client,
285                          &handle_address_response,
286                          rh,
287                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
288 }
289
290
291 /**
292  * We've been asked to lookup the address for a hostname and were 
293  * given a valid numeric string.  Perform the callbacks for the
294  * numeric addresses.
295  *
296  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
297  * @param tc unused scheduler context
298  */
299 static void
300 numeric_resolution (void *cls,
301                     const struct GNUNET_SCHEDULER_TaskContext *tc)
302 {
303   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
304   struct sockaddr_in v4;
305   struct sockaddr_in6 v6;
306
307   memset (&v4, 0, sizeof(v4));
308   v4.sin_family = AF_INET;
309 #if HAVE_SOCKADDR_IN_SIN_LEN
310   v4.sin_len = sizeof(v4);
311 #endif
312   memset (&v6, 0, sizeof(v6)); 
313   v6.sin6_family = AF_INET6;
314 #if HAVE_SOCKADDR_IN_SIN_LEN
315   v6.sin6_len = sizeof(v6);
316 #endif
317
318   if ( ( (rh->domain == AF_UNSPEC) || (rh->domain == AF_INET) ) && 
319        (1 == inet_pton (AF_INET,
320                         rh->hostname,
321                         &v4.sin_addr)) )
322     {
323       rh->addr_callback (rh->cls,
324                            (const struct sockaddr*) &v4,
325                            sizeof(v4));
326       if ( (rh->domain == AF_UNSPEC) && 
327            (1 == inet_pton (AF_INET6,
328                             rh->hostname,
329                             &v6.sin6_addr)) )
330         {
331           /* this can happen on some systems IF "hostname" is "localhost" */
332           rh->addr_callback (rh->cls,
333                                (const struct sockaddr*) &v6,
334                                sizeof(v6));
335         }
336       rh->addr_callback (rh->cls, NULL, 0);      
337       GNUNET_free (rh);
338       return;
339     }
340   if ( ( (rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6) ) && 
341        (1 == inet_pton (AF_INET6,
342                         rh->hostname,
343                         &v6.sin6_addr)) )
344     {
345       rh->addr_callback (rh->cls,
346                            (const struct sockaddr*) &v6,
347                            sizeof(v6));
348       rh->addr_callback (rh->cls, NULL, 0);
349       GNUNET_free (rh);
350       return;
351     }
352   /* why are we here? this task should not have been scheduled! */
353   GNUNET_assert (0);
354   GNUNET_free (rh);
355 }
356
357
358
359 /**
360  * We've been asked to lookup the address for a hostname and were 
361  * given a variant of "loopback".  Perform the callbacks for the
362  * respective loopback numeric addresses.
363  *
364  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
365  * @param tc unused scheduler context
366  */
367 static void
368 loopback_resolution (void *cls,
369                      const struct GNUNET_SCHEDULER_TaskContext *tc)
370 {
371   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
372   struct sockaddr_in v4;
373   struct sockaddr_in6 v6;
374
375   memset (&v4, 0, sizeof(v4));
376   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
377   v4.sin_family = AF_INET;
378 #if HAVE_SOCKADDR_IN_SIN_LEN
379   v4.sin_len = sizeof(v4);
380 #endif
381   memset (&v6, 0, sizeof(v6)); 
382   v6.sin6_family = AF_INET6;
383 #if HAVE_SOCKADDR_IN_SIN_LEN
384   v6.sin6_len = sizeof(v6);
385 #endif
386   v6.sin6_addr = in6addr_loopback;
387   switch (rh->domain)
388     {
389     case AF_INET:
390       rh->addr_callback (rh->cls, 
391                            (const struct sockaddr*) &v4,
392                            sizeof(v4));
393       break;
394     case AF_INET6:
395       rh->addr_callback (rh->cls, 
396                            (const struct sockaddr*) &v6,
397                            sizeof(v6));
398       break;
399     case AF_UNSPEC:
400       rh->addr_callback (rh->cls, 
401                            (const struct sockaddr*) &v6,
402                            sizeof(v6));
403       rh->addr_callback (rh->cls, 
404                            (const struct sockaddr*) &v4,
405                            sizeof(v4));
406       break;
407     default:
408       GNUNET_break (0);
409       break;
410     }
411   rh->addr_callback (rh->cls, NULL, 0);
412   GNUNET_free (rh);
413 }
414
415
416 /**
417  * Convert a string to one or more IP addresses.
418  *
419  * @param sched scheduler to use
420  * @param cfg configuration to use
421  * @param hostname the hostname to resolve
422  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
423  * @param callback function to call with addresses
424  * @param callback_cls closure for callback
425  * @param timeout how long to try resolving
426  * @return handle that can be used to cancel the request, NULL on error
427  */
428 struct GNUNET_RESOLVER_RequestHandle *
429 GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
430                         const struct GNUNET_CONFIGURATION_Handle *cfg,
431                         const char *hostname,
432                         int domain,
433                         struct GNUNET_TIME_Relative timeout,
434                         GNUNET_RESOLVER_AddressCallback callback, 
435                         void *callback_cls)
436 {
437   struct GNUNET_CLIENT_Connection *client;
438   struct GNUNET_RESOLVER_GetMessage *msg;
439   struct GNUNET_RESOLVER_RequestHandle *rh;
440   size_t slen;
441   unsigned int i;
442   struct in_addr v4;
443   struct in6_addr v6;
444   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
445
446   check_config (cfg);
447   slen = strlen (hostname) + 1;
448   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
449       GNUNET_SERVER_MAX_MESSAGE_SIZE)
450     {
451       GNUNET_break (0);
452       return NULL;
453     }
454   rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + slen);
455   rh->client = client;
456   rh->sched = sched;
457   rh->domain = domain;
458   rh->addr_callback = callback;
459   rh->cls = callback_cls;
460   memcpy (&rh[1], hostname, slen);
461   rh->hostname = (const char*) &rh[1];
462   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
463
464   /* first, check if this is a numeric address */
465   if ( ( (1 == inet_pton (AF_INET,
466                           hostname,
467                           &v4)) &&
468          ( (domain == AF_INET) || (domain == AF_UNSPEC)) ) ||
469        ( (1 == inet_pton (AF_INET6,
470                           hostname,
471                           &v6)) &&
472          ( (domain == AF_INET6) || (domain == AF_UNSPEC)) ) )
473     {
474       rh->task = GNUNET_SCHEDULER_add_delayed (sched,
475                                                GNUNET_NO,
476                                                GNUNET_SCHEDULER_PRIORITY_KEEP,
477                                                GNUNET_SCHEDULER_NO_TASK,
478                                                GNUNET_TIME_UNIT_ZERO,
479                                                &numeric_resolution,
480                                                rh);
481       return rh;
482     }
483   /* then, check if this is a loopback address */
484   i = 0;
485   while (loopback[i] != NULL)
486     if (0 == strcasecmp (loopback[i++], hostname))
487       {
488         rh->task = GNUNET_SCHEDULER_add_delayed (sched,
489                                                  GNUNET_NO,
490                                                  GNUNET_SCHEDULER_PRIORITY_KEEP,
491                                                  GNUNET_SCHEDULER_NO_TASK,
492                                                  GNUNET_TIME_UNIT_ZERO,
493                                                  &loopback_resolution,
494                                                  rh);
495         return rh;
496       }
497
498   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
499   if (client == NULL)
500     {
501       GNUNET_free (rh);
502       return NULL;    
503     }
504   rh->client = client;
505
506   msg = (struct GNUNET_RESOLVER_GetMessage*) buf;
507   msg->header.size =
508     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
509   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
510   msg->direction = htonl (GNUNET_NO);
511   msg->domain = htonl (domain);
512   memcpy (&msg[1], hostname, slen);
513
514 #if DEBUG_RESOLVER
515   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
516               _("Resolver requests DNS resolution of hostname `%s'.\n"),
517               hostname);
518 #endif
519   if (GNUNET_OK !=
520       GNUNET_CLIENT_transmit_and_get_response (client,
521                                                &msg->header,
522                                                timeout,
523                                                GNUNET_YES,
524                                                &handle_address_response,
525                                                rh))
526     {
527       GNUNET_free (rh);
528       GNUNET_CLIENT_disconnect (client);
529       return NULL;
530     }
531   return rh;
532 }
533
534
535 /**
536  * Process response with a hostname for a reverse DNS lookup.
537  *
538  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
539  * @param msg message with the hostname, NULL on error
540  */
541 static void
542 handle_hostname_response (void *cls,
543                           const struct GNUNET_MessageHeader *msg)
544 {
545   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
546   uint16_t size;
547   const char *hostname;
548
549   if (msg == NULL)
550     {
551       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
552                   _("Timeout trying to resolve IP address.\n"));
553       rh->name_callback (rh->cls, NULL);
554       GNUNET_CLIENT_disconnect (rh->client);
555       GNUNET_free (rh);
556       return;
557     }
558   size = ntohs (msg->size);
559   if (size == sizeof (struct GNUNET_MessageHeader))
560     {
561 #if DEBUG_RESOLVER
562       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563                   _("Received end message resolving IP address.\n"));
564 #endif
565       rh->name_callback (rh->cls, NULL);
566       GNUNET_CLIENT_disconnect (rh->client);
567       GNUNET_free (rh);
568       return;
569     }
570   hostname = (const char *) &msg[1];
571   if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
572     {
573       GNUNET_break (0);
574       rh->name_callback (rh->cls, NULL);
575       GNUNET_CLIENT_disconnect (rh->client);
576       GNUNET_free (rh);
577       return;
578     }
579 #if DEBUG_RESOLVER
580   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581               _("Resolver returns `%s'.\n"), hostname);
582 #endif
583   rh->name_callback (rh->cls, hostname);
584   GNUNET_CLIENT_receive (rh->client,
585                          &handle_hostname_response,
586                          rh,
587                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
588 }
589
590
591
592 /**
593  * We've been asked to convert an address to a string without
594  * a reverse lookup.  Do it.
595  *
596  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
597  * @param tc unused scheduler context
598  */
599 static void
600 numeric_reverse (void *cls,
601                  const struct GNUNET_SCHEDULER_TaskContext *tc)
602 {
603   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
604   char *result;
605
606   result = no_resolve ((const struct sockaddr*) &rh[1], 
607                        rh->salen);
608 #if DEBUG_RESOLVER
609   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610               _("Resolver returns `%s'.\n"), result);
611 #endif
612   if (result != NULL)
613     {
614       rh->name_callback (rh->cls, result);
615       GNUNET_free (result);
616     }
617   rh->name_callback (rh->cls, NULL);
618   GNUNET_free (rh);
619 }
620
621
622
623 /**
624  * Get an IP address as a string.
625  *
626  * @param sched scheduler to use
627  * @param cfg configuration to use
628  * @param sa host address
629  * @param salen length of host address
630  * @param do_resolve use GNUNET_NO to return numeric hostname
631  * @param timeout how long to try resolving
632  * @param callback function to call with hostnames
633  * @param cls closure for callback
634  * @return handle that can be used to cancel the request
635  */
636 struct GNUNET_RESOLVER_RequestHandle *
637 GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
638                               const struct GNUNET_CONFIGURATION_Handle *cfg,
639                               const struct sockaddr *sa,
640                               socklen_t salen,
641                               int do_resolve,
642                               struct GNUNET_TIME_Relative timeout,
643                               GNUNET_RESOLVER_HostnameCallback callback,
644                               void *cls)
645 {
646   struct GNUNET_CLIENT_Connection *client;
647   struct GNUNET_RESOLVER_GetMessage *msg;
648   struct GNUNET_RESOLVER_RequestHandle *rh;
649   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
650
651   check_config (cfg);
652   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
653   rh->name_callback = callback;
654   rh->cls = cls;
655   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
656   rh->sched = sched;
657   rh->salen = salen;
658   memcpy (&rh[1], sa, salen);
659
660   if (GNUNET_NO == do_resolve)
661     {
662       rh->task = GNUNET_SCHEDULER_add_delayed (sched,
663                                                GNUNET_NO,
664                                                GNUNET_SCHEDULER_PRIORITY_KEEP,
665                                                GNUNET_SCHEDULER_NO_TASK,
666                                                GNUNET_TIME_UNIT_ZERO,
667                                                &numeric_reverse,
668                                                rh);      
669       return rh;
670     }
671   if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
672       GNUNET_SERVER_MAX_MESSAGE_SIZE)
673     {
674       GNUNET_break (0);
675       GNUNET_free (rh);
676       return NULL;
677     }
678   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
679   if (client == NULL)
680     {
681       GNUNET_free (rh);
682       return NULL;
683     }
684   rh->client = client;
685
686   msg = (struct GNUNET_RESOLVER_GetMessage*) buf;
687   msg->header.size =
688     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
689   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
690   msg->direction = htonl (GNUNET_YES);
691   msg->domain = htonl (sa->sa_family);
692   memcpy (&msg[1], sa, salen);
693 #if DEBUG_RESOLVER
694   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695               _("Resolver requests DNS resolution of IP address.\n"));
696 #endif
697   if (GNUNET_OK !=
698       GNUNET_CLIENT_transmit_and_get_response (client,
699                                                &msg->header,
700                                                timeout,
701                                                GNUNET_YES,
702                                                &handle_hostname_response,
703                                                rh))
704     {
705       GNUNET_CLIENT_disconnect (client);
706       GNUNET_free (rh);
707       return NULL;
708     }
709   return rh;
710 }
711
712
713 /**
714  * Perform a reverse DNS lookup.
715  *
716  * @param sched scheduler to use
717  * @param cfg configuration to use
718  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
719  * @param callback function to call with addresses
720  * @param cls closure for callback
721  * @param timeout how long to try resolving
722  * @return handle that can be used to cancel the request, NULL on error
723  */
724 struct GNUNET_RESOLVER_RequestHandle *
725 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
726                                   const struct GNUNET_CONFIGURATION_Handle *cfg,
727                                   int domain,
728                                   struct GNUNET_TIME_Relative timeout,
729                                   GNUNET_RESOLVER_AddressCallback callback,
730                                   void *cls)
731 {
732   char hostname[MAX_HOSTNAME];
733
734   check_config (cfg);
735   if (0 != gethostname (hostname, sizeof (hostname) - 1))
736     {
737       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
738                            GNUNET_ERROR_TYPE_BULK, "gethostname");
739       return NULL;
740     }
741 #if DEBUG_RESOLVER
742   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743               _("Resolving our hostname `%s'\n"), hostname);
744 #endif
745   return GNUNET_RESOLVER_ip_get (sched,
746                                  cfg, hostname, domain, timeout, callback, cls);
747 }
748
749
750 /**
751  * Cancel a request that is still pending with the resolver.
752  * Note that a client MUST NOT cancel a request that has
753  * been completed (i.e, the callback has been called to
754  * signal timeout or the final result).
755  *
756  * @param h handle of request to cancel
757  */
758 void
759 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *h)
760 {
761   if (h->client != NULL)
762     GNUNET_CLIENT_disconnect (h->client);
763   if (h->task != GNUNET_SCHEDULER_NO_TASK)
764     GNUNET_SCHEDULER_cancel (h->sched,
765                              h->task);
766   GNUNET_free (h);
767 }
768
769
770
771 /* end of resolver_api.c */