2 This file is part of GNUnet.
3 (C) 2007, 2008, 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 resolver/gnunet-service-resolver.c
23 * @brief code to do DNS resolution
24 * @author Christian Grothoff
29 #include "gnunet_disk_lib.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_strings_lib.h"
35 #include "gnunet_time_lib.h"
44 struct GNUNET_TIME_Absolute last_refresh;
45 struct GNUNET_TIME_Absolute last_request;
50 static struct IPCache *head;
57 getnameinfo_resolve (struct IPCache *cache)
61 if (0 == getnameinfo (cache->sa, cache->salen, hostname, 255, NULL, 0, 0))
62 cache->addr = GNUNET_strdup (hostname);
67 #if HAVE_GETHOSTBYADDR
69 gethostbyaddr_resolve (struct IPCache *cache)
73 switch (cache->sa->sa_family)
76 ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr,
77 sizeof (struct in_addr), AF_INET);
80 ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr,
81 sizeof (struct in6_addr), AF_INET6);
87 cache->addr = GNUNET_strdup (ent->h_name);
93 cache_resolve (struct IPCache *cache)
96 if (cache->addr == NULL)
97 getnameinfo_resolve (cache);
99 #if HAVE_GETHOSTBYADDR
100 if (cache->addr == NULL)
101 gethostbyaddr_resolve (cache);
108 * Get an IP address as a string (works for both IPv4 and IPv6). Note
109 * that the resolution happens asynchronously and that the first call
110 * may not immediately result in the FQN (but instead in a
111 * human-readable IP address).
113 * @param sa should be of type "struct sockaddr*"
116 get_ip_as_string (struct GNUNET_SERVER_Client *client,
117 const struct sockaddr *sav, socklen_t salen)
119 struct IPCache *cache;
120 struct IPCache *prev;
121 struct GNUNET_TIME_Absolute now;
122 struct GNUNET_SERVER_TransmitContext *tc;
124 if (salen < sizeof (struct sockaddr))
129 now = GNUNET_TIME_absolute_get ();
132 while ((cache != NULL) &&
133 ((cache->salen != salen) || (0 != memcmp (cache->sa, sav, salen))))
135 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
140 prev->next = cache->next;
141 GNUNET_free_non_null (cache->addr);
142 GNUNET_free (cache->sa);
149 GNUNET_free_non_null (cache->addr);
150 GNUNET_free (cache->sa);
161 cache->last_request = now;
162 if (GNUNET_TIME_absolute_get_duration (cache->last_request).value <
165 GNUNET_free_non_null (cache->addr);
168 cache_resolve (cache);
173 cache = GNUNET_malloc (sizeof (struct IPCache));
175 cache->salen = salen;
176 cache->sa = GNUNET_malloc (salen);
177 memcpy (cache->sa, sav, salen);
178 cache->last_request = GNUNET_TIME_absolute_get ();
179 cache->last_refresh = GNUNET_TIME_absolute_get ();
181 cache_resolve (cache);
184 tc = GNUNET_SERVER_transmit_context_create (client);
185 if (cache->addr != NULL)
186 GNUNET_SERVER_transmit_context_append (tc,
188 strlen (cache->addr) + 1,
189 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
190 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
191 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
192 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
198 getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc,
199 const char *hostname, int domain)
202 struct addrinfo hints;
203 struct addrinfo *result;
204 struct addrinfo *pos;
206 memset (&hints, 0, sizeof (struct addrinfo));
209 hints.ai_family = domain;
211 hints.ai_family = AF_INET;
213 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
215 if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
217 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
218 _("Could not resolve `%s' (%s): %s\n"), hostname,
220 AF_INET) ? "IPv4" : ((domain ==
221 AF_INET6) ? "IPv6" : "any"),
223 if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) ||
231 return GNUNET_NO; /* other function may still succeed */
232 return GNUNET_SYSERR;
235 return GNUNET_SYSERR;
239 GNUNET_SERVER_transmit_context_append (tc,
242 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
245 freeaddrinfo (result);
250 #if HAVE_GETHOSTBYNAME2
252 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
253 const char *hostname, int domain)
256 struct sockaddr_in a4;
257 struct sockaddr_in6 a6;
261 if (domain == AF_UNSPEC)
263 ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
264 ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
265 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
267 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
268 return GNUNET_SYSERR;
271 hp = gethostbyname2 (hostname, domain);
274 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275 _("Could not find IP of host `%s': %s\n"),
276 hostname, hstrerror (h_errno));
277 return GNUNET_SYSERR;
279 GNUNET_assert (hp->h_addrtype == domain);
280 if (domain == AF_INET)
282 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
283 memset (&a4, 0, sizeof (a4));
284 a4.sin_family = AF_INET;
285 memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length);
286 GNUNET_SERVER_transmit_context_append (tc,
289 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
293 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
294 memset (&a6, 0, sizeof (a6));
295 a6.sin6_family = AF_INET6;
296 memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length);
297 GNUNET_SERVER_transmit_context_append (tc,
300 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
306 #if HAVE_GETHOSTBYNAME
308 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
309 const char *hostname)
312 struct sockaddr_in addr;
314 hp = GETHOSTBYNAME (hostname);
317 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
318 _("Could not find IP of host `%s': %s\n"),
319 hostname, hstrerror (h_errno));
320 return GNUNET_SYSERR;
322 if (hp->h_addrtype != AF_INET)
325 return GNUNET_SYSERR;
327 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
328 memset (&addr, 0, sizeof (addr));
329 addr.sin_family = AF_INET;
330 memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length);
331 GNUNET_SERVER_transmit_context_append (tc,
334 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
341 * Convert a string to an IP address.
343 * @param client where to send the IP address
344 * @param hostname the hostname to resolve
345 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
348 get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
349 const char *hostname, int domain)
352 struct GNUNET_SERVER_TransmitContext *tc;
354 tc = GNUNET_SERVER_transmit_context_create (client);
357 if (ret == GNUNET_NO)
358 ret = getaddrinfo_resolve (tc, hostname, domain);
360 #if HAVE_GETHOSTBYNAME2
361 if (ret == GNUNET_NO)
362 ret = gethostbyname2_resolve (tc, hostname, domain);
364 #if HAVE_GETHOSTBYNAME
365 if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET)))
366 gethostbyname_resolve (tc, hostname);
368 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
369 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
370 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
375 * Handle GET-message.
378 * @param client identification of the client
379 * @param message the actual message
382 handle_get (void *cls,
383 struct GNUNET_SERVER_Client *client,
384 const struct GNUNET_MessageHeader *message)
387 const struct GNUNET_RESOLVER_GetMessage *msg;
388 const char *hostname;
393 msize = ntohs (message->size);
394 if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
397 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
400 msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
401 size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
402 direction = ntohl (msg->direction);
403 domain = ntohl (msg->domain);
404 if (direction == GNUNET_NO)
406 /* IP from hostname */
407 hostname = (const char *) &msg[1];
408 if (hostname[size - 1] != '\0')
411 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 _("Resolver asked to look up `%s'.\n"), hostname);
418 get_ip_from_hostname (client, hostname, domain);
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 _("Resolver asked to look up IP address.\n"));
426 get_ip_as_string (client, (const struct sockaddr *) &msg[1], size);
432 * List of handlers for the messages understood by this
435 static struct GNUNET_SERVER_MessageHandler handlers[] = {
436 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
442 * Process resolver requests.
445 * @param sched scheduler to use
446 * @param server the initialized server
447 * @param cfg configuration to use
451 struct GNUNET_SCHEDULER_Handle *sched,
452 struct GNUNET_SERVER_Handle *server,
453 const struct GNUNET_CONFIGURATION_Handle *cfg)
455 GNUNET_SERVER_add_handlers (server, handlers);
460 * The main function for the resolver service.
462 * @param argc number of arguments from the command line
463 * @param argv command line arguments
464 * @return 0 ok, 1 on error
467 main (int argc, char *const *argv)
473 GNUNET_SERVICE_run (argc,
475 "resolver", &run, NULL, NULL, NULL)) ? 0 : 1;
480 GNUNET_free_non_null (head->addr);
481 GNUNET_free (head->sa);
488 /* end of gnunet-service-resolver.c */