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