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_client_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_resolver_service.h"
31 #include "gnunet_server_lib.h"
36 * Maximum supported length for a hostname
38 #define MAX_HOSTNAME 1024
42 * Possible hostnames for "loopback".
44 static const char *loopback[] = {
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.
56 struct GNUNET_RESOLVER_RequestHandle
60 * Callback if this is an name resolution request,
63 GNUNET_RESOLVER_AddressCallback addr_callback;
66 * Callback if this is a reverse lookup request,
69 GNUNET_RESOLVER_HostnameCallback name_callback;
72 * Closure for the respective "callback".
77 * Our connection to the resolver service.
79 struct GNUNET_CLIENT_Connection *client;
84 struct GNUNET_SCHEDULER_Handle *sched;
87 * Name of the host that we are resolving.
92 * When should this request time out?
94 struct GNUNET_TIME_Absolute timeout;
97 * Task handle for numeric lookups.
99 GNUNET_SCHEDULER_TaskIdentifier task;
102 * Desired address family.
107 * Length of the "struct sockaddr" that follows this
108 * struct (only for reverse lookup).
115 * Check that the resolver service runs on localhost
119 check_config (const struct GNUNET_CONFIGURATION_Handle *cfg)
123 struct sockaddr_in v4;
124 struct sockaddr_in6 v6;
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);
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);
138 GNUNET_CONFIGURATION_get_value_string (cfg,
143 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144 _("Must specify `%s' for `%s' in configuration!\n"),
149 if ( (1 != inet_pton (AF_INET,
152 (1 != inet_pton (AF_INET6,
156 GNUNET_free (hostname);
160 while (loopback[i] != NULL)
161 if (0 == strcasecmp (loopback[i++], hostname))
163 GNUNET_free (hostname);
166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167 _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
171 GNUNET_free (hostname);
177 * Convert IP address to string without DNS resolution.
179 * @param sa the address
180 * @param salen number of bytes in sa
181 * @return address as a string, NULL on error
184 no_resolve (const struct sockaddr *sa, socklen_t salen)
187 char inet4[INET_ADDRSTRLEN];
188 char inet6[INET6_ADDRSTRLEN];
190 if (salen < sizeof (struct sockaddr))
192 switch (sa->sa_family)
195 if (salen != sizeof (struct sockaddr_in))
198 &((struct sockaddr_in *) sa)->sin_addr,
199 inet4, INET_ADDRSTRLEN);
200 ret = GNUNET_strdup (inet4);
203 if (salen != sizeof (struct sockaddr_in6))
206 &((struct sockaddr_in6 *) sa)->sin6_addr,
207 inet6, INET6_ADDRSTRLEN);
208 ret = GNUNET_strdup (inet6);
219 * Process the reply from the resolver (which is presumably
220 * the numeric IP address for a name resolution request).
222 * @param cls the "GNUNET_RESOLVER_RequestHandle" for which this is a reply
223 * @param msg reply from the resolver or NULL on error
226 handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
228 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
230 const struct sockaddr *sa;
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);
243 if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
246 rh->addr_callback (rh->cls, NULL, 0);
247 GNUNET_CLIENT_disconnect (rh->client);
252 size = ntohs (msg->size);
253 if (size == sizeof (struct GNUNET_MessageHeader))
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257 _("Received end message resolving hostname.\n"));
259 rh->addr_callback (rh->cls, NULL, 0);
260 GNUNET_CLIENT_disconnect (rh->client);
264 sa = (const struct sockaddr *) &msg[1];
265 salen = size - sizeof (struct GNUNET_MessageHeader);
266 if (salen < sizeof (struct sockaddr))
269 rh->addr_callback (rh->cls, NULL, 0);
270 GNUNET_CLIENT_disconnect (rh->client);
276 char *ips = no_resolve (sa, salen);
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278 "Resolver returns `%s'.\n",
283 rh->addr_callback (rh->cls, sa, salen);
284 GNUNET_CLIENT_receive (rh->client,
285 &handle_address_response,
287 GNUNET_TIME_absolute_get_remaining (rh->timeout));
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
296 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
297 * @param tc unused scheduler context
300 numeric_resolution (void *cls,
301 const struct GNUNET_SCHEDULER_TaskContext *tc)
303 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
304 struct sockaddr_in v4;
305 struct sockaddr_in6 v6;
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);
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);
318 if ( ( (rh->domain == AF_UNSPEC) || (rh->domain == AF_INET) ) &&
319 (1 == inet_pton (AF_INET,
323 rh->addr_callback (rh->cls,
324 (const struct sockaddr*) &v4,
326 if ( (rh->domain == AF_UNSPEC) &&
327 (1 == inet_pton (AF_INET6,
331 /* this can happen on some systems IF "hostname" is "localhost" */
332 rh->addr_callback (rh->cls,
333 (const struct sockaddr*) &v6,
336 rh->addr_callback (rh->cls, NULL, 0);
340 if ( ( (rh->domain == AF_UNSPEC) || (rh->domain == AF_INET6) ) &&
341 (1 == inet_pton (AF_INET6,
345 rh->addr_callback (rh->cls,
346 (const struct sockaddr*) &v6,
348 rh->addr_callback (rh->cls, NULL, 0);
352 /* why are we here? this task should not have been scheduled! */
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.
364 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
365 * @param tc unused scheduler context
368 loopback_resolution (void *cls,
369 const struct GNUNET_SCHEDULER_TaskContext *tc)
371 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
372 struct sockaddr_in v4;
373 struct sockaddr_in6 v6;
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);
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);
386 v6.sin6_addr = in6addr_loopback;
390 rh->addr_callback (rh->cls,
391 (const struct sockaddr*) &v4,
395 rh->addr_callback (rh->cls,
396 (const struct sockaddr*) &v6,
400 rh->addr_callback (rh->cls,
401 (const struct sockaddr*) &v6,
403 rh->addr_callback (rh->cls,
404 (const struct sockaddr*) &v4,
411 rh->addr_callback (rh->cls, NULL, 0);
417 * Convert a string to one or more IP addresses.
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
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,
433 struct GNUNET_TIME_Relative timeout,
434 GNUNET_RESOLVER_AddressCallback callback,
437 struct GNUNET_CLIENT_Connection *client;
438 struct GNUNET_RESOLVER_GetMessage *msg;
439 struct GNUNET_RESOLVER_RequestHandle *rh;
444 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
447 slen = strlen (hostname) + 1;
448 if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
449 GNUNET_SERVER_MAX_MESSAGE_SIZE)
454 rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + slen);
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);
464 /* first, check if this is a numeric address */
465 if ( ( (1 == inet_pton (AF_INET,
468 ( (domain == AF_INET) || (domain == AF_UNSPEC)) ) ||
469 ( (1 == inet_pton (AF_INET6,
472 ( (domain == AF_INET6) || (domain == AF_UNSPEC)) ) )
474 rh->task = GNUNET_SCHEDULER_add_delayed (sched,
476 GNUNET_SCHEDULER_PRIORITY_KEEP,
477 GNUNET_SCHEDULER_NO_TASK,
478 GNUNET_TIME_UNIT_ZERO,
483 /* then, check if this is a loopback address */
485 while (loopback[i] != NULL)
486 if (0 == strcasecmp (loopback[i++], hostname))
488 rh->task = GNUNET_SCHEDULER_add_delayed (sched,
490 GNUNET_SCHEDULER_PRIORITY_KEEP,
491 GNUNET_SCHEDULER_NO_TASK,
492 GNUNET_TIME_UNIT_ZERO,
493 &loopback_resolution,
498 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
506 msg = (struct GNUNET_RESOLVER_GetMessage*) buf;
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);
515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
516 _("Resolver requests DNS resolution of hostname `%s'.\n"),
520 GNUNET_CLIENT_transmit_and_get_response (client,
524 &handle_address_response,
528 GNUNET_CLIENT_disconnect (client);
536 * Process response with a hostname for a reverse DNS lookup.
538 * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
539 * @param msg message with the hostname, NULL on error
542 handle_hostname_response (void *cls,
543 const struct GNUNET_MessageHeader *msg)
545 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
547 const char *hostname;
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);
558 size = ntohs (msg->size);
559 if (size == sizeof (struct GNUNET_MessageHeader))
562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563 _("Received end message resolving IP address.\n"));
565 rh->name_callback (rh->cls, NULL);
566 GNUNET_CLIENT_disconnect (rh->client);
570 hostname = (const char *) &msg[1];
571 if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
574 rh->name_callback (rh->cls, NULL);
575 GNUNET_CLIENT_disconnect (rh->client);
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581 _("Resolver returns `%s'.\n"), hostname);
583 rh->name_callback (rh->cls, hostname);
584 GNUNET_CLIENT_receive (rh->client,
585 &handle_hostname_response,
587 GNUNET_TIME_absolute_get_remaining (rh->timeout));
593 * We've been asked to convert an address to a string without
594 * a reverse lookup. Do it.
596 * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
597 * @param tc unused scheduler context
600 numeric_reverse (void *cls,
601 const struct GNUNET_SCHEDULER_TaskContext *tc)
603 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
606 result = no_resolve ((const struct sockaddr*) &rh[1],
609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610 _("Resolver returns `%s'.\n"), result);
614 rh->name_callback (rh->cls, result);
615 GNUNET_free (result);
617 rh->name_callback (rh->cls, NULL);
624 * Get an IP address as a string.
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
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,
642 struct GNUNET_TIME_Relative timeout,
643 GNUNET_RESOLVER_HostnameCallback callback,
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];
652 rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
653 rh->name_callback = callback;
655 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
658 memcpy (&rh[1], sa, salen);
660 if (GNUNET_NO == do_resolve)
662 rh->task = GNUNET_SCHEDULER_add_delayed (sched,
664 GNUNET_SCHEDULER_PRIORITY_KEEP,
665 GNUNET_SCHEDULER_NO_TASK,
666 GNUNET_TIME_UNIT_ZERO,
671 if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
672 GNUNET_SERVER_MAX_MESSAGE_SIZE)
678 client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
686 msg = (struct GNUNET_RESOLVER_GetMessage*) buf;
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);
694 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695 _("Resolver requests DNS resolution of IP address.\n"));
698 GNUNET_CLIENT_transmit_and_get_response (client,
702 &handle_hostname_response,
705 GNUNET_CLIENT_disconnect (client);
714 * Perform a reverse DNS lookup.
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
724 struct GNUNET_RESOLVER_RequestHandle *
725 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
726 const struct GNUNET_CONFIGURATION_Handle *cfg,
728 struct GNUNET_TIME_Relative timeout,
729 GNUNET_RESOLVER_AddressCallback callback,
732 char hostname[MAX_HOSTNAME];
735 if (0 != gethostname (hostname, sizeof (hostname) - 1))
737 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
738 GNUNET_ERROR_TYPE_BULK, "gethostname");
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743 _("Resolving our hostname `%s'\n"), hostname);
745 return GNUNET_RESOLVER_ip_get (sched,
746 cfg, hostname, domain, timeout, callback, cls);
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).
756 * @param h handle of request to cancel
759 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *h)
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,
771 /* end of resolver_api.c */