2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file util/resolver_api.c
23 * @brief resolver for writing a tool
24 * @author Christian Grothoff
27 #include "gnunet_getopt_lib.h"
28 #include "gnunet_os_lib.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_resolver_service.h"
32 #include "gnunet_server_lib.h"
37 * Maximum supported length for a hostname
39 #define MAX_HOSTNAME 1024
43 * Possible hostnames for "loopback".
45 static const char *loopback[] = {
53 * Handle to a request given to the resolver. Can be used to cancel
54 * the request prior to the timeout or successful execution. Also
55 * used to track our internal state for the request.
57 struct GNUNET_RESOLVER_RequestHandle
61 * Callback if this is an name resolution request,
64 GNUNET_RESOLVER_AddressCallback addr_callback;
67 * Callback if this is a reverse lookup request,
70 GNUNET_RESOLVER_HostnameCallback name_callback;
73 * Closure for the respective "callback".
78 * Our connection to the resolver service.
80 struct GNUNET_CLIENT_Connection *client;
85 struct GNUNET_SCHEDULER_Handle *sched;
88 * Name of the host that we are resolving.
93 * When should this request time out?
95 struct GNUNET_TIME_Absolute timeout;
98 * Task handle for numeric lookups.
100 GNUNET_SCHEDULER_TaskIdentifier task;
103 * Desired address family.
108 * Length of the "struct sockaddr" that follows this
109 * struct (only for reverse lookup).
116 * Check that the resolver service runs on localhost
120 check_config (const struct GNUNET_CONFIGURATION_Handle *cfg)
124 struct sockaddr_in v4;
125 struct sockaddr_in6 v6;
127 memset (&v4, 0, sizeof (v4));
128 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
129 v4.sin_family = AF_INET;
130 #if HAVE_SOCKADDR_IN_SIN_LEN
131 v4.sin_len = sizeof (v4);
133 memset (&v6, 0, sizeof (v6));
134 v6.sin6_family = AF_INET6;
135 #if HAVE_SOCKADDR_IN_SIN_LEN
136 v6.sin6_len = sizeof (v6);
139 GNUNET_CONFIGURATION_get_value_string (cfg,
141 "HOSTNAME", &hostname))
143 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144 _("Must specify `%s' for `%s' in configuration!\n"),
145 "HOSTNAME", "resolver");
148 if ((1 != inet_pton (AF_INET,
150 &v4)) || (1 != inet_pton (AF_INET6, hostname, &v6)))
152 GNUNET_free (hostname);
156 while (loopback[i] != NULL)
157 if (0 == strcasecmp (loopback[i++], hostname))
159 GNUNET_free (hostname);
162 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
164 ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
165 "localhost", "HOSTNAME", "resolver");
166 GNUNET_free (hostname);
172 * Convert IP address to string without DNS resolution.
174 * @param sa the address
175 * @param salen number of bytes in sa
176 * @return address as a string, NULL on error
179 no_resolve (const struct sockaddr *sa, socklen_t salen)
182 char inet4[INET_ADDRSTRLEN];
183 char inet6[INET6_ADDRSTRLEN];
185 if (salen < sizeof (struct sockaddr))
187 switch (sa->sa_family)
190 if (salen != sizeof (struct sockaddr_in))
194 &((struct sockaddr_in *) sa)->sin_addr,
195 inet4, INET_ADDRSTRLEN))
197 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
200 ret = GNUNET_strdup (inet4);
203 if (salen != sizeof (struct sockaddr_in6))
207 &((struct sockaddr_in6 *) sa)->sin6_addr,
208 inet6, INET6_ADDRSTRLEN))
210 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
213 ret = GNUNET_strdup (inet6);
224 * Process the reply from the resolver (which is presumably
225 * the numeric IP address for a name resolution request).
227 * @param cls the "GNUNET_RESOLVER_RequestHandle" for which this is a reply
228 * @param msg reply from the resolver or NULL on error
231 handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
233 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
235 const struct sockaddr *sa;
240 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
241 _("Timeout trying to resolve hostname `%s'.\n"),
243 rh->addr_callback (rh->cls, NULL, 0);
244 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
248 if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
251 rh->addr_callback (rh->cls, NULL, 0);
252 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
257 size = ntohs (msg->size);
258 if (size == sizeof (struct GNUNET_MessageHeader))
261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262 _("Received end message resolving hostname `%s'.\n"),
265 rh->addr_callback (rh->cls, NULL, 0);
266 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
270 sa = (const struct sockaddr *) &msg[1];
271 salen = size - sizeof (struct GNUNET_MessageHeader);
272 if (salen < sizeof (struct sockaddr))
275 rh->addr_callback (rh->cls, NULL, 0);
276 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
282 char *ips = no_resolve (sa, salen);
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284 "Resolver returns `%s' for `%s'.\n", ips,
289 rh->addr_callback (rh->cls, sa, salen);
290 GNUNET_CLIENT_receive (rh->client,
291 &handle_address_response,
293 GNUNET_TIME_absolute_get_remaining (rh->timeout));
298 * We've been asked to lookup the address for a hostname and were
299 * given a valid numeric string. Perform the callbacks for the
302 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
303 * @param tc unused scheduler context
306 numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
308 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
309 struct sockaddr_in v4;
310 struct sockaddr_in6 v6;
312 memset (&v4, 0, sizeof (v4));
313 v4.sin_family = AF_INET;
314 #if HAVE_SOCKADDR_IN_SIN_LEN
315 v4.sin_len = sizeof (v4);
317 memset (&v6, 0, sizeof (v6));
318 v6.sin6_family = AF_INET6;
319 #if HAVE_SOCKADDR_IN_SIN_LEN
320 v6.sin6_len = sizeof (v6);
323 if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET)) &&
324 (1 == inet_pton (AF_INET, rh->hostname, &v4.sin_addr)))
326 rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
327 if ((rh->domain == AF_UNSPEC) &&
328 (1 == inet_pton (AF_INET6, rh->hostname, &v6.sin6_addr)))
330 /* this can happen on some systems IF "hostname" is "localhost" */
331 rh->addr_callback (rh->cls,
332 (const struct sockaddr *) &v6, sizeof (v6));
334 rh->addr_callback (rh->cls, NULL, 0);
338 if (((rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6)) &&
339 (1 == inet_pton (AF_INET6, rh->hostname, &v6.sin6_addr)))
341 rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
342 rh->addr_callback (rh->cls, NULL, 0);
346 /* why are we here? this task should not have been scheduled! */
354 * We've been asked to lookup the address for a hostname and were
355 * given a variant of "loopback". Perform the callbacks for the
356 * respective loopback numeric addresses.
358 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
359 * @param tc unused scheduler context
362 loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
364 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
365 struct sockaddr_in v4;
366 struct sockaddr_in6 v6;
368 memset (&v4, 0, sizeof (v4));
369 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
370 v4.sin_family = AF_INET;
371 #if HAVE_SOCKADDR_IN_SIN_LEN
372 v4.sin_len = sizeof (v4);
374 memset (&v6, 0, sizeof (v6));
375 v6.sin6_family = AF_INET6;
376 #if HAVE_SOCKADDR_IN_SIN_LEN
377 v6.sin6_len = sizeof (v6);
379 v6.sin6_addr = in6addr_loopback;
383 rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
386 rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
389 rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
390 rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
396 rh->addr_callback (rh->cls, NULL, 0);
402 * Convert a string to one or more IP addresses.
404 * @param sched scheduler to use
405 * @param cfg configuration to use
406 * @param hostname the hostname to resolve
407 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
408 * @param callback function to call with addresses
409 * @param callback_cls closure for callback
410 * @param timeout how long to try resolving
411 * @return handle that can be used to cancel the request, NULL on error
413 struct GNUNET_RESOLVER_RequestHandle *
414 GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
415 const struct GNUNET_CONFIGURATION_Handle *cfg,
416 const char *hostname,
418 struct GNUNET_TIME_Relative timeout,
419 GNUNET_RESOLVER_AddressCallback callback,
422 struct GNUNET_CLIENT_Connection *client;
423 struct GNUNET_RESOLVER_GetMessage *msg;
424 struct GNUNET_RESOLVER_RequestHandle *rh;
429 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
432 slen = strlen (hostname) + 1;
433 if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
438 rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
441 rh->addr_callback = callback;
442 rh->cls = callback_cls;
443 memcpy (&rh[1], hostname, slen);
444 rh->hostname = (const char *) &rh[1];
445 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
447 /* first, check if this is a numeric address */
448 if (((1 == inet_pton (AF_INET,
451 ((domain == AF_INET) || (domain == AF_UNSPEC))) ||
452 ((1 == inet_pton (AF_INET6,
455 ((domain == AF_INET6) || (domain == AF_UNSPEC))))
457 rh->task = GNUNET_SCHEDULER_add_now (sched,
458 &numeric_resolution, rh);
461 /* then, check if this is a loopback address */
463 while (loopback[i] != NULL)
464 if (0 == strcasecmp (loopback[i++], hostname))
466 rh->task = GNUNET_SCHEDULER_add_now (sched,
467 &loopback_resolution, rh);
471 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
479 msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
481 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
482 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
483 msg->direction = htonl (GNUNET_NO);
484 msg->domain = htonl (domain);
485 memcpy (&msg[1], hostname, slen);
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 _("Resolver requests DNS resolution of hostname `%s'.\n"),
493 GNUNET_CLIENT_transmit_and_get_response (client,
497 &handle_address_response, rh))
500 GNUNET_CLIENT_disconnect (client, GNUNET_NO);
508 * Process response with a hostname for a reverse DNS lookup.
510 * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
511 * @param msg message with the hostname, NULL on error
514 handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
516 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
518 const char *hostname;
522 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
523 _("Timeout trying to resolve IP address `%s'.\n"),
524 GNUNET_a2s ((const void*) &rh[1], rh->salen));
525 rh->name_callback (rh->cls, NULL);
526 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
530 size = ntohs (msg->size);
531 if (size == sizeof (struct GNUNET_MessageHeader))
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 _("Received end message resolving IP address `%s'.\n"),
536 GNUNET_a2s ((const void*) &rh[1], rh->salen));
538 rh->name_callback (rh->cls, NULL);
539 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
543 hostname = (const char *) &msg[1];
544 if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
547 rh->name_callback (rh->cls, NULL);
548 GNUNET_CLIENT_disconnect (rh->client, GNUNET_NO);
553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 _("Resolver returns `%s' for IP `%s'.\n"),
556 GNUNET_a2s ((const void*) &rh[1], rh->salen));
558 rh->name_callback (rh->cls, hostname);
559 GNUNET_CLIENT_receive (rh->client,
560 &handle_hostname_response,
562 GNUNET_TIME_absolute_get_remaining (rh->timeout));
568 * We've been asked to convert an address to a string without
569 * a reverse lookup. Do it.
571 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
572 * @param tc unused scheduler context
575 numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
577 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
580 result = no_resolve ((const struct sockaddr *) &rh[1], rh->salen);
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
586 rh->name_callback (rh->cls, result);
587 GNUNET_free (result);
589 rh->name_callback (rh->cls, NULL);
596 * Get an IP address as a string.
598 * @param sched scheduler to use
599 * @param cfg configuration to use
600 * @param sa host address
601 * @param salen length of host address
602 * @param do_resolve use GNUNET_NO to return numeric hostname
603 * @param timeout how long to try resolving
604 * @param callback function to call with hostnames
605 * @param cls closure for callback
606 * @return handle that can be used to cancel the request
608 struct GNUNET_RESOLVER_RequestHandle *
609 GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
610 const struct GNUNET_CONFIGURATION_Handle *cfg,
611 const struct sockaddr *sa,
614 struct GNUNET_TIME_Relative timeout,
615 GNUNET_RESOLVER_HostnameCallback callback,
618 struct GNUNET_CLIENT_Connection *client;
619 struct GNUNET_RESOLVER_GetMessage *msg;
620 struct GNUNET_RESOLVER_RequestHandle *rh;
621 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
624 rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
625 rh->name_callback = callback;
627 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
630 memcpy (&rh[1], sa, salen);
632 if (GNUNET_NO == do_resolve)
634 rh->task = GNUNET_SCHEDULER_add_now (sched,
635 &numeric_reverse, rh);
638 if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
644 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
652 msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
654 htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
655 msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
656 msg->direction = htonl (GNUNET_YES);
657 msg->domain = htonl (sa->sa_family);
658 memcpy (&msg[1], sa, salen);
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 _("Resolver requests DNS resolution of IP address.\n"));
664 GNUNET_CLIENT_transmit_and_get_response (client,
668 &handle_hostname_response, rh))
670 GNUNET_CLIENT_disconnect (client, GNUNET_NO);
679 * Get local fully qualified domain name
683 GNUNET_RESOLVER_local_fqdn_get ( void )
685 struct hostent *host;
686 char hostname[GNUNET_OS_get_hostname_max_length() + 1];
689 if (0 != gethostname (hostname, sizeof (hostname) - 1))
691 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
692 GNUNET_ERROR_TYPE_BULK, "gethostname");
696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697 _("Resolving our FQDN `%s'\n"), hostname);
699 host = gethostbyname ( hostname );
702 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
703 _("Could not resolve our FQDN : %s\n"),
704 hstrerror (h_errno));
707 return GNUNET_strdup (host->h_name);
711 * Looking our own hostname.
713 * @param sched scheduler to use
714 * @param cfg configuration to use
715 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
716 * @param callback function to call with addresses
717 * @param cls closure for callback
718 * @param timeout how long to try resolving
719 * @return handle that can be used to cancel the request, NULL on error
721 struct GNUNET_RESOLVER_RequestHandle *
722 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
723 const struct GNUNET_CONFIGURATION_Handle
725 struct GNUNET_TIME_Relative timeout,
726 GNUNET_RESOLVER_AddressCallback callback,
729 char hostname[GNUNET_OS_get_hostname_max_length() + 1];
732 if (0 != gethostname (hostname, sizeof (hostname) - 1))
734 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
735 GNUNET_ERROR_TYPE_BULK, "gethostname");
739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
740 _("Resolving our hostname `%s'\n"), hostname);
742 return GNUNET_RESOLVER_ip_get (sched,
743 cfg, hostname, domain, timeout, callback,
749 * Cancel a request that is still pending with the resolver.
750 * Note that a client MUST NOT cancel a request that has
751 * been completed (i.e, the callback has been called to
752 * signal timeout or the final result).
754 * @param h handle of request to cancel
757 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *h)
759 if (h->client != NULL)
760 GNUNET_CLIENT_disconnect (h->client, GNUNET_NO);
761 if (h->task != GNUNET_SCHEDULER_NO_TASK)
762 GNUNET_SCHEDULER_cancel (h->sched, h->task);
768 /* end of resolver_api.c */