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) || (s == EAI_SYSTEM))
224 return GNUNET_NO; /* other function may still succeed */
225 return GNUNET_SYSERR;
228 return GNUNET_SYSERR;
232 GNUNET_SERVER_transmit_context_append (tc,
235 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
238 freeaddrinfo (result);
243 #if HAVE_GETHOSTBYNAME2
245 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
246 const char *hostname, int domain)
249 struct sockaddr_in a4;
250 struct sockaddr_in6 a6;
254 if (domain == AF_UNSPEC)
256 ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
257 ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
258 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
260 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
261 return GNUNET_SYSERR;
264 hp = gethostbyname2 (hostname, domain);
267 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
268 _("Could not find IP of host `%s': %s\n"),
269 hostname, hstrerror (h_errno));
270 return GNUNET_SYSERR;
272 GNUNET_assert (hp->h_addrtype == domain);
273 if (domain == AF_INET)
275 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
276 memset (&a4, 0, sizeof (a4));
277 a4.sin_family = AF_INET;
278 memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length);
279 GNUNET_SERVER_transmit_context_append (tc,
282 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
286 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
287 memset (&a6, 0, sizeof (a6));
288 a6.sin6_family = AF_INET6;
289 memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length);
290 GNUNET_SERVER_transmit_context_append (tc,
293 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
299 #if HAVE_GETHOSTBYNAME
301 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
302 const char *hostname)
305 struct sockaddr_in addr;
307 hp = GETHOSTBYNAME (hostname);
310 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
311 _("Could not find IP of host `%s': %s\n"),
312 hostname, hstrerror (h_errno));
313 return GNUNET_SYSERR;
315 if (hp->h_addrtype != AF_INET)
318 return GNUNET_SYSERR;
320 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
321 memset (&addr, 0, sizeof (addr));
322 addr.sin_family = AF_INET;
323 memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length);
324 GNUNET_SERVER_transmit_context_append (tc,
327 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
334 * Convert a string to an IP address.
336 * @param client where to send the IP address
337 * @param hostname the hostname to resolve
338 * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
341 get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
342 const char *hostname, int domain)
345 struct GNUNET_SERVER_TransmitContext *tc;
347 tc = GNUNET_SERVER_transmit_context_create (client);
350 if (ret == GNUNET_NO)
351 ret = getaddrinfo_resolve (tc, hostname, domain);
353 #if HAVE_GETHOSTBYNAME2
354 if (ret == GNUNET_NO)
355 ret = gethostbyname2_resolve (tc, hostname, domain);
357 #if HAVE_GETHOSTBYNAME
358 if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET)))
359 gethostbyname_resolve (tc, hostname);
361 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
362 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
363 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
368 * Handle GET-message.
371 * @param client identification of the client
372 * @param message the actual message
375 handle_get (void *cls,
376 struct GNUNET_SERVER_Client *client,
377 const struct GNUNET_MessageHeader *message)
380 const struct GNUNET_RESOLVER_GetMessage *msg;
381 const char *hostname;
386 msize = ntohs (message->size);
387 if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
390 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
393 msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
394 size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
395 direction = ntohl (msg->direction);
396 domain = ntohl (msg->domain);
397 if (direction == GNUNET_NO)
399 /* IP from hostname */
400 hostname = (const char *) &msg[1];
401 if (hostname[size - 1] != '\0')
404 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409 _("Resolver asked to look up `%s'.\n"), hostname);
411 get_ip_from_hostname (client, hostname, domain);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 _("Resolver asked to look up IP address.\n"));
419 get_ip_as_string (client, (const struct sockaddr *) &msg[1], size);
425 * List of handlers for the messages understood by this
428 static struct GNUNET_SERVER_MessageHandler handlers[] = {
429 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
435 * Process statistics requests.
438 * @param sched scheduler to use
439 * @param server the initialized server
440 * @param cfg configuration to use
444 struct GNUNET_SCHEDULER_Handle *sched,
445 struct GNUNET_SERVER_Handle *server,
446 struct GNUNET_CONFIGURATION_Handle *cfg)
448 GNUNET_SERVER_add_handlers (server, handlers);
453 * The main function for the resolver service.
455 * @param argc number of arguments from the command line
456 * @param argv command line arguments
457 * @return 0 ok, 1 on error
460 main (int argc, char *const *argv)
466 GNUNET_SERVICE_run (argc,
468 "resolver", &run, NULL, NULL, NULL)) ? 0 : 1;
473 GNUNET_free_non_null (head->addr);
474 GNUNET_free (head->sa);
481 /* end of gnunet-service-resolver.c */