stuff
[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", &hostname))
141     {
142       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
143                   _("Must specify `%s' for `%s' in configuration!\n"),
144                   "HOSTNAME", "resolver");
145       GNUNET_assert (0);
146     }
147   if ((1 != inet_pton (AF_INET,
148                        hostname,
149                        &v4)) || (1 != inet_pton (AF_INET6, hostname, &v6)))
150     {
151       GNUNET_free (hostname);
152       return;
153     }
154   i = 0;
155   while (loopback[i] != NULL)
156     if (0 == strcasecmp (loopback[i++], hostname))
157       {
158         GNUNET_free (hostname);
159         return;
160       }
161   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
162               _
163               ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
164               "localhost", "HOSTNAME", "resolver");
165   GNUNET_free (hostname);
166   GNUNET_assert (0);
167 }
168
169
170 /**
171  * Convert IP address to string without DNS resolution.
172  *
173  * @param sa the address 
174  * @param salen number of bytes in sa
175  * @return address as a string, NULL on error
176  */
177 static char *
178 no_resolve (const struct sockaddr *sa, socklen_t salen)
179 {
180   char *ret;
181   char inet4[INET_ADDRSTRLEN];
182   char inet6[INET6_ADDRSTRLEN];
183
184   if (salen < sizeof (struct sockaddr))
185     return NULL;
186   switch (sa->sa_family)
187     {
188     case AF_INET:
189       if (salen != sizeof (struct sockaddr_in))
190         return NULL;
191       inet_ntop (AF_INET,
192                  &((struct sockaddr_in *) sa)->sin_addr,
193                  inet4, INET_ADDRSTRLEN);
194       ret = GNUNET_strdup (inet4);
195       break;
196     case AF_INET6:
197       if (salen != sizeof (struct sockaddr_in6))
198         return NULL;
199       inet_ntop (AF_INET6,
200                  &((struct sockaddr_in6 *) sa)->sin6_addr,
201                  inet6, INET6_ADDRSTRLEN);
202       ret = GNUNET_strdup (inet6);
203       break;
204     default:
205       ret = NULL;
206       break;
207     }
208   return ret;
209 }
210
211
212 /**
213  * Process the reply from the resolver (which is presumably
214  * the numeric IP address for a name resolution request).
215  *
216  * @param cls the "GNUNET_RESOLVER_RequestHandle" for which this is a reply
217  * @param msg reply from the resolver or NULL on error
218  */
219 static void
220 handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
221 {
222   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
223   uint16_t size;
224   const struct sockaddr *sa;
225   socklen_t salen;
226
227
228   if (msg == NULL)
229     {
230       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
231                   _("Timeout trying to resolve hostname `%s'.\n"),
232                   rh->hostname);
233       rh->addr_callback (rh->cls, NULL, 0);
234       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
235       GNUNET_free (rh);
236       return;
237     }
238   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
239     {
240       GNUNET_break (0);
241       rh->addr_callback (rh->cls, NULL, 0);
242       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
243       GNUNET_free (rh);
244       return;
245     }
246
247   size = ntohs (msg->size);
248   if (size == sizeof (struct GNUNET_MessageHeader))
249     {
250 #if DEBUG_RESOLVER
251       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
252                   _("Received end message resolving hostname `%s'.\n"),
253                   rh->hostname);
254 #endif
255       rh->addr_callback (rh->cls, NULL, 0);
256       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
257       GNUNET_free (rh);
258       return;
259     }
260   sa = (const struct sockaddr *) &msg[1];
261   salen = size - sizeof (struct GNUNET_MessageHeader);
262   if (salen < sizeof (struct sockaddr))
263     {
264       GNUNET_break (0);
265       rh->addr_callback (rh->cls, NULL, 0);
266       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
267       GNUNET_free (rh);
268       return;
269     }
270 #if DEBUG_RESOLVER
271   {
272     char *ips = no_resolve (sa, salen);
273     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274                 "Resolver returns `%s' for `%s'.\n", ips,
275                 rh->hostname);
276     GNUNET_free (ips);
277   }
278 #endif
279   rh->addr_callback (rh->cls, sa, salen);
280   GNUNET_CLIENT_receive (rh->client,
281                          &handle_address_response,
282                          rh,
283                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
284 }
285
286
287 /**
288  * We've been asked to lookup the address for a hostname and were 
289  * given a valid numeric string.  Perform the callbacks for the
290  * numeric addresses.
291  *
292  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
293  * @param tc unused scheduler context
294  */
295 static void
296 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
297 {
298   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
299   struct sockaddr_in v4;
300   struct sockaddr_in6 v6;
301
302   memset (&v4, 0, sizeof (v4));
303   v4.sin_family = AF_INET;
304 #if HAVE_SOCKADDR_IN_SIN_LEN
305   v4.sin_len = sizeof (v4);
306 #endif
307   memset (&v6, 0, sizeof (v6));
308   v6.sin6_family = AF_INET6;
309 #if HAVE_SOCKADDR_IN_SIN_LEN
310   v6.sin6_len = sizeof (v6);
311 #endif
312
313   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET)) &&
314       (1 == inet_pton (AF_INET, rh->hostname, &v4.sin_addr)))
315     {
316       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
317       if ((rh->domain == AF_UNSPEC) &&
318           (1 == inet_pton (AF_INET6, rh->hostname, &v6.sin6_addr)))
319         {
320           /* this can happen on some systems IF "hostname" is "localhost" */
321           rh->addr_callback (rh->cls,
322                              (const struct sockaddr *) &v6, sizeof (v6));
323         }
324       rh->addr_callback (rh->cls, NULL, 0);
325       GNUNET_free (rh);
326       return;
327     }
328   if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6)) &&
329       (1 == inet_pton (AF_INET6, rh->hostname, &v6.sin6_addr)))
330     {
331       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
332       rh->addr_callback (rh->cls, NULL, 0);
333       GNUNET_free (rh);
334       return;
335     }
336   /* why are we here? this task should not have been scheduled! */
337   GNUNET_assert (0);
338   GNUNET_free (rh);
339 }
340
341
342
343 /**
344  * We've been asked to lookup the address for a hostname and were 
345  * given a variant of "loopback".  Perform the callbacks for the
346  * respective loopback numeric addresses.
347  *
348  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
349  * @param tc unused scheduler context
350  */
351 static void
352 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
353 {
354   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
355   struct sockaddr_in v4;
356   struct sockaddr_in6 v6;
357
358   memset (&v4, 0, sizeof (v4));
359   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
360   v4.sin_family = AF_INET;
361 #if HAVE_SOCKADDR_IN_SIN_LEN
362   v4.sin_len = sizeof (v4);
363 #endif
364   memset (&v6, 0, sizeof (v6));
365   v6.sin6_family = AF_INET6;
366 #if HAVE_SOCKADDR_IN_SIN_LEN
367   v6.sin6_len = sizeof (v6);
368 #endif
369   v6.sin6_addr = in6addr_loopback;
370   switch (rh->domain)
371     {
372     case AF_INET:
373       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
374       break;
375     case AF_INET6:
376       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
377       break;
378     case AF_UNSPEC:
379       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
380       rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
381       break;
382     default:
383       GNUNET_break (0);
384       break;
385     }
386   rh->addr_callback (rh->cls, NULL, 0);
387   GNUNET_free (rh);
388 }
389
390
391 /**
392  * Convert a string to one or more IP addresses.
393  *
394  * @param sched scheduler to use
395  * @param cfg configuration to use
396  * @param hostname the hostname to resolve
397  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
398  * @param callback function to call with addresses
399  * @param callback_cls closure for callback
400  * @param timeout how long to try resolving
401  * @return handle that can be used to cancel the request, NULL on error
402  */
403 struct GNUNET_RESOLVER_RequestHandle *
404 GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
405                         const struct GNUNET_CONFIGURATION_Handle *cfg,
406                         const char *hostname,
407                         int domain,
408                         struct GNUNET_TIME_Relative timeout,
409                         GNUNET_RESOLVER_AddressCallback callback,
410                         void *callback_cls)
411 {
412   struct GNUNET_CLIENT_Connection *client;
413   struct GNUNET_RESOLVER_GetMessage *msg;
414   struct GNUNET_RESOLVER_RequestHandle *rh;
415   size_t slen;
416   unsigned int i;
417   struct in_addr v4;
418   struct in6_addr v6;
419   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
420
421   check_config (cfg);
422   slen = strlen (hostname) + 1;
423   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
424       GNUNET_SERVER_MAX_MESSAGE_SIZE)
425     {
426       GNUNET_break (0);
427       return NULL;
428     }
429   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
430   rh->sched = sched;
431   rh->domain = domain;
432   rh->addr_callback = callback;
433   rh->cls = callback_cls;
434   memcpy (&rh[1], hostname, slen);
435   rh->hostname = (const char *) &rh[1];
436   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
437
438   /* first, check if this is a numeric address */
439   if (((1 == inet_pton (AF_INET,
440                         hostname,
441                         &v4)) &&
442        ((domain == AF_INET) || (domain == AF_UNSPEC))) ||
443       ((1 == inet_pton (AF_INET6,
444                         hostname,
445                         &v6)) &&
446        ((domain == AF_INET6) || (domain == AF_UNSPEC))))
447     {
448       rh->task = GNUNET_SCHEDULER_add_now (sched,
449                                            &numeric_resolution, rh);
450       return rh;
451     }
452   /* then, check if this is a loopback address */
453   i = 0;
454   while (loopback[i] != NULL)
455     if (0 == strcasecmp (loopback[i++], hostname))
456       {
457         rh->task = GNUNET_SCHEDULER_add_now (sched,
458                                              &loopback_resolution, rh);
459         return rh;
460       }
461
462   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
463   if (client == NULL)
464     {
465       GNUNET_free (rh);
466       return NULL;
467     }
468   rh->client = client;
469
470   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
471   msg->header.size =
472     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
473   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
474   msg->direction = htonl (GNUNET_NO);
475   msg->domain = htonl (domain);
476   memcpy (&msg[1], hostname, slen);
477
478 #if DEBUG_RESOLVER
479   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
480               _("Resolver requests DNS resolution of hostname `%s'.\n"),
481               hostname);
482 #endif
483   if (GNUNET_OK !=
484       GNUNET_CLIENT_transmit_and_get_response (client,
485                                                &msg->header,
486                                                timeout,
487                                                GNUNET_YES,
488                                                &handle_address_response, rh))
489     {
490       GNUNET_free (rh);
491       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
492       return NULL;
493     }
494   return rh;
495 }
496
497
498 /**
499  * Process response with a hostname for a reverse DNS lookup.
500  *
501  * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
502  * @param msg message with the hostname, NULL on error
503  */
504 static void
505 handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
506 {
507   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
508   uint16_t size;
509   const char *hostname;
510
511   if (msg == NULL)
512     {
513       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
514                   _("Timeout trying to resolve IP address `%s'.\n"),
515                   GNUNET_a2s ((const void*) &rh[1], rh->salen));
516       rh->name_callback (rh->cls, NULL);
517       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
518       GNUNET_free (rh);
519       return;
520     }
521   size = ntohs (msg->size);
522   if (size == sizeof (struct GNUNET_MessageHeader))
523     {
524 #if DEBUG_RESOLVER
525       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526                   _("Received end message resolving IP address `%s'.\n"),
527                   GNUNET_a2s ((const void*) &rh[1], rh->salen));
528 #endif
529       rh->name_callback (rh->cls, NULL);
530       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
531       GNUNET_free (rh);
532       return;
533     }
534   hostname = (const char *) &msg[1];
535   if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
536     {
537       GNUNET_break (0);
538       rh->name_callback (rh->cls, NULL);
539       GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
540       GNUNET_free (rh);
541       return;
542     }
543 #if DEBUG_RESOLVER
544   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545               _("Resolver returns `%s' for IP `%s'.\n"), 
546               hostname,
547               GNUNET_a2s ((const void*) &rh[1], rh->salen));
548 #endif
549   rh->name_callback (rh->cls, hostname);
550   GNUNET_CLIENT_receive (rh->client,
551                          &handle_hostname_response,
552                          rh,
553                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
554 }
555
556
557
558 /**
559  * We've been asked to convert an address to a string without
560  * a reverse lookup.  Do it.
561  *
562  * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
563  * @param tc unused scheduler context
564  */
565 static void
566 numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
567 {
568   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
569   char *result;
570
571   result = no_resolve ((const struct sockaddr *) &rh[1], rh->salen);
572 #if DEBUG_RESOLVER
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
574 #endif
575   if (result != NULL)
576     {
577       rh->name_callback (rh->cls, result);
578       GNUNET_free (result);
579     }
580   rh->name_callback (rh->cls, NULL);
581   GNUNET_free (rh);
582 }
583
584
585
586 /**
587  * Get an IP address as a string.
588  *
589  * @param sched scheduler to use
590  * @param cfg configuration to use
591  * @param sa host address
592  * @param salen length of host address
593  * @param do_resolve use GNUNET_NO to return numeric hostname
594  * @param timeout how long to try resolving
595  * @param callback function to call with hostnames
596  * @param cls closure for callback
597  * @return handle that can be used to cancel the request
598  */
599 struct GNUNET_RESOLVER_RequestHandle *
600 GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
601                               const struct GNUNET_CONFIGURATION_Handle *cfg,
602                               const struct sockaddr *sa,
603                               socklen_t salen,
604                               int do_resolve,
605                               struct GNUNET_TIME_Relative timeout,
606                               GNUNET_RESOLVER_HostnameCallback callback,
607                               void *cls)
608 {
609   struct GNUNET_CLIENT_Connection *client;
610   struct GNUNET_RESOLVER_GetMessage *msg;
611   struct GNUNET_RESOLVER_RequestHandle *rh;
612   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
613
614   check_config (cfg);
615   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
616   rh->name_callback = callback;
617   rh->cls = cls;
618   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
619   rh->sched = sched;
620   rh->salen = salen;
621   memcpy (&rh[1], sa, salen);
622
623   if (GNUNET_NO == do_resolve)
624     {
625       rh->task = GNUNET_SCHEDULER_add_now (sched,
626                                            &numeric_reverse, rh);
627       return rh;
628     }
629   if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
630       GNUNET_SERVER_MAX_MESSAGE_SIZE)
631     {
632       GNUNET_break (0);
633       GNUNET_free (rh);
634       return NULL;
635     }
636   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
637   if (client == NULL)
638     {
639       GNUNET_free (rh);
640       return NULL;
641     }
642   rh->client = client;
643
644   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
645   msg->header.size =
646     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
647   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
648   msg->direction = htonl (GNUNET_YES);
649   msg->domain = htonl (sa->sa_family);
650   memcpy (&msg[1], sa, salen);
651 #if DEBUG_RESOLVER
652   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653               _("Resolver requests DNS resolution of IP address.\n"));
654 #endif
655   if (GNUNET_OK !=
656       GNUNET_CLIENT_transmit_and_get_response (client,
657                                                &msg->header,
658                                                timeout,
659                                                GNUNET_YES,
660                                                &handle_hostname_response, rh))
661     {
662       GNUNET_CLIENT_disconnect (client, GNUNET_NO);
663       GNUNET_free (rh);
664       return NULL;
665     }
666   return rh;
667 }
668
669
670 /**
671  * Perform a reverse DNS lookup.
672  *
673  * @param sched scheduler to use
674  * @param cfg configuration to use
675  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
676  * @param callback function to call with addresses
677  * @param cls closure for callback
678  * @param timeout how long to try resolving
679  * @return handle that can be used to cancel the request, NULL on error
680  */
681 struct GNUNET_RESOLVER_RequestHandle *
682 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
683                                   const struct GNUNET_CONFIGURATION_Handle
684                                   *cfg, int domain,
685                                   struct GNUNET_TIME_Relative timeout,
686                                   GNUNET_RESOLVER_AddressCallback callback,
687                                   void *cls)
688 {
689   char hostname[MAX_HOSTNAME];
690
691   check_config (cfg);
692   if (0 != gethostname (hostname, sizeof (hostname) - 1))
693     {
694       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
695                            GNUNET_ERROR_TYPE_BULK, "gethostname");
696       return NULL;
697     }
698 #if DEBUG_RESOLVER
699   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700               _("Resolving our hostname `%s'\n"), hostname);
701 #endif
702   return GNUNET_RESOLVER_ip_get (sched,
703                                  cfg, hostname, domain, timeout, callback,
704                                  cls);
705 }
706
707
708 /**
709  * Cancel a request that is still pending with the resolver.
710  * Note that a client MUST NOT cancel a request that has
711  * been completed (i.e, the callback has been called to
712  * signal timeout or the final result).
713  *
714  * @param h handle of request to cancel
715  */
716 void
717 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *h)
718 {
719   if (h->client != NULL)
720     GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
721   if (h->task != GNUNET_SCHEDULER_NO_TASK)
722     GNUNET_SCHEDULER_cancel (h->sched, h->task);
723   GNUNET_free (h);
724 }
725
726
727
728 /* end of resolver_api.c */