2 This file is part of GNUnet.
3 Copyright (C) 2007-2013 GNUnet e.V.
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 3, 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file util/gnunet-service-resolver.c
23 * @brief code to do DNS resolution
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_statistics_service.h"
33 * A cached DNS lookup result (for reverse lookup).
38 * This is a doubly linked list.
43 * This is a doubly linked list.
48 * Hostname in human-readable form.
53 * Binary IP address, allocated at the end of this struct.
58 * Last time this entry was updated.
60 struct GNUNET_TIME_Absolute last_refresh;
63 * Last time this entry was requested.
65 struct GNUNET_TIME_Absolute last_request;
68 * Number of bytes in ip.
73 * Address family of the IP.
80 * Start of the linked list of cached DNS lookup results.
82 static struct IPCache *cache_head;
85 * Tail of the linked list of cached DNS lookup results.
87 static struct IPCache *cache_tail;
92 * Resolve the given request using getnameinfo
94 * @param cache the request to resolve (and where to store the result)
97 getnameinfo_resolve (struct IPCache *cache)
100 const struct sockaddr *sa;
101 struct sockaddr_in v4;
102 struct sockaddr_in6 v6;
109 GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
110 sa = (const struct sockaddr*) &v4;
111 memset (&v4, 0, sizeof (v4));
112 v4.sin_addr = * (const struct in_addr*) cache->ip;
113 v4.sin_family = AF_INET;
114 #if HAVE_SOCKADDR_IN_SIN_LEN
115 v4.sin_len = sizeof (v4);
120 GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
121 sa = (const struct sockaddr*) &v6;
122 memset (&v6, 0, sizeof (v6));
123 v6.sin6_addr = * (const struct in6_addr*) cache->ip;
124 v6.sin6_family = AF_INET6;
125 #if HAVE_SOCKADDR_IN_SIN_LEN
126 v6.sin6_len = sizeof (v6);
135 (ret = getnameinfo (sa, salen,
136 hostname, sizeof (hostname),
140 cache->addr = GNUNET_strdup (hostname);
144 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
145 "getnameinfo failed: %s\n",
152 #if HAVE_GETHOSTBYADDR
154 * Resolve the given request using gethostbyaddr
156 * @param cache the request to resolve (and where to store the result)
159 gethostbyaddr_resolve (struct IPCache *cache)
163 ent = gethostbyaddr (cache->ip,
168 cache->addr = GNUNET_strdup (ent->h_name);
172 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
173 "gethostbyaddr failed: %s\n",
174 hstrerror (h_errno));
181 * Resolve the given request using the available methods.
183 * @param cache the request to resolve (and where to store the result)
186 cache_resolve (struct IPCache *cache)
189 if (NULL == cache->addr)
190 getnameinfo_resolve (cache);
192 #if HAVE_GETHOSTBYADDR
193 if (NULL == cache->addr)
194 gethostbyaddr_resolve (cache);
200 * Get an IP address as a string (works for both IPv4 and IPv6). Note
201 * that the resolution happens asynchronously and that the first call
202 * may not immediately result in the FQN (but instead in a
203 * human-readable IP address).
205 * @param client handle to the client making the request (for sending the reply)
206 * @param af AF_INET or AF_INET6
207 * @param ip `struct in_addr` or `struct in6_addr`
210 get_ip_as_string (struct GNUNET_SERVER_Client *client,
215 struct IPCache *next;
216 struct GNUNET_TIME_Absolute now;
217 struct GNUNET_SERVER_TransmitContext *tc;
224 ip_len = sizeof (struct in_addr);
227 ip_len = sizeof (struct in6_addr);
232 now = GNUNET_TIME_absolute_get ();
234 while ( (NULL != (pos = next)) &&
236 (pos->ip_len != ip_len) ||
237 (0 != memcmp (pos->ip, ip, ip_len))) )
240 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
241 60 * 60 * 1000 * 1000LL)
243 GNUNET_CONTAINER_DLL_remove (cache_head,
246 GNUNET_free_non_null (pos->addr);
253 if ( (1 == inet_pton (af,
256 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
257 120 * 1000 * 1000LL) )
259 /* try again if still numeric AND 2 minutes have expired */
260 GNUNET_free_non_null (pos->addr);
263 pos->last_request = now;
268 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
270 GNUNET_memcpy (&pos[1], ip, ip_len);
271 pos->last_request = now;
272 pos->last_refresh = now;
273 pos->ip_len = ip_len;
275 GNUNET_CONTAINER_DLL_insert (cache_head,
280 tc = GNUNET_SERVER_transmit_context_create (client);
281 if (NULL != pos->addr)
282 GNUNET_SERVER_transmit_context_append_data (tc, pos->addr,
283 strlen (pos->addr) + 1,
284 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
287 "Reverse lookup failed\n");
288 GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
289 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
290 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
296 getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc,
297 const char *hostname, int af)
300 struct addrinfo hints;
301 struct addrinfo *result;
302 struct addrinfo *pos;
305 /* Due to a bug, getaddrinfo will not return a mix of different families */
310 ret1 = getaddrinfo_resolve (tc, hostname, AF_INET);
311 ret2 = getaddrinfo_resolve (tc, hostname, AF_INET6);
312 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
314 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
315 return GNUNET_SYSERR;
320 memset (&hints, 0, sizeof (struct addrinfo));
321 hints.ai_family = af;
322 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
324 if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
326 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
327 _("Could not resolve `%s' (%s): %s\n"),
330 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
332 if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY)
339 return GNUNET_NO; /* other function may still succeed */
340 return GNUNET_SYSERR;
343 return GNUNET_SYSERR;
344 for (pos = result; pos != NULL; pos = pos->ai_next)
346 switch (pos->ai_family)
349 GNUNET_SERVER_transmit_context_append_data (tc,
350 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
351 sizeof (struct in_addr),
352 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
355 GNUNET_SERVER_transmit_context_append_data (tc,
356 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
357 sizeof (struct in6_addr),
358 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
361 /* unsupported, skip */
365 freeaddrinfo (result);
370 #elif HAVE_GETHOSTBYNAME2
374 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
375 const char *hostname,
383 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
389 ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
390 ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
391 if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
393 if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
394 return GNUNET_SYSERR;
397 hp = gethostbyname2 (hostname, af);
400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 _("Could not find IP of host `%s': %s\n"), hostname,
402 hstrerror (h_errno));
403 return GNUNET_SYSERR;
405 GNUNET_assert (hp->h_addrtype == af);
409 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
410 GNUNET_SERVER_transmit_context_append_data (tc,
413 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
416 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
417 GNUNET_SERVER_transmit_context_append_data (tc,
420 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
424 return GNUNET_SYSERR;
429 #elif HAVE_GETHOSTBYNAME
433 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
434 const char *hostname)
438 hp = GETHOSTBYNAME (hostname);
441 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
442 _("Could not find IP of host `%s': %s\n"),
444 hstrerror (h_errno));
445 return GNUNET_SYSERR;
447 if (hp->h_addrtype != AF_INET)
450 return GNUNET_SYSERR;
452 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
453 GNUNET_SERVER_transmit_context_append_data (tc,
456 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
463 * Convert a string to an IP address.
465 * @param client where to send the IP address
466 * @param hostname the hostname to resolve
467 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
470 get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
471 const char *hostname,
475 struct GNUNET_SERVER_TransmitContext *tc;
477 tc = GNUNET_SERVER_transmit_context_create (client);
480 if (ret == GNUNET_NO)
481 ret = getaddrinfo_resolve (tc, hostname, af);
482 #elif HAVE_GETHOSTBYNAME2
483 if (ret == GNUNET_NO)
484 ret = gethostbyname2_resolve (tc, hostname, af);
485 #elif HAVE_GETHOSTBYNAME
486 if ((ret == GNUNET_NO) && ((af == AF_UNSPEC) || (af == PF_INET)))
487 gethostbyname_resolve (tc, hostname);
489 GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
490 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
491 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
496 * Handle GET-message.
499 * @param client identification of the client
500 * @param message the actual message
503 handle_get (void *cls,
504 struct GNUNET_SERVER_Client *client,
505 const struct GNUNET_MessageHeader *message)
508 const struct GNUNET_RESOLVER_GetMessage *msg;
514 msize = ntohs (message->size);
515 if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
518 GNUNET_SERVER_receive_done (client,
522 msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
523 size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
524 direction = ntohl (msg->direction);
525 af = ntohl (msg->af);
526 if (GNUNET_NO == direction)
528 /* IP from hostname */
529 const char *hostname;
531 hostname = (const char *) &msg[1];
532 if (hostname[size - 1] != '\0')
535 GNUNET_SERVER_receive_done (client,
539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
540 "Resolver asked to look up `%s'.\n",
542 get_ip_from_hostname (client, hostname, af);
549 if (size != sizeof (struct in_addr))
552 GNUNET_SERVER_receive_done (client,
558 if (size != sizeof (struct in6_addr))
561 GNUNET_SERVER_receive_done (client,
568 GNUNET_SERVER_receive_done (client,
573 char buf[INET6_ADDRSTRLEN];
575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576 "Resolver asked to look up IP address `%s'.\n",
577 inet_ntop (af, ip, buf, sizeof (buf)));
579 get_ip_as_string (client, af, ip);
584 * Process resolver requests.
587 * @param server the initialized server
588 * @param cfg configuration to use
591 run (void *cls, struct GNUNET_SERVER_Handle *server,
592 const struct GNUNET_CONFIGURATION_Handle *cfg)
594 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
595 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
598 GNUNET_SERVER_add_handlers (server, handlers);
603 * The main function for the resolver service.
605 * @param argc number of arguments from the command line
606 * @param argv command line arguments
607 * @return 0 ok, 1 on error
610 main (int argc, char *const *argv)
617 GNUNET_SERVICE_run (argc, argv,
619 GNUNET_SERVICE_OPTION_NONE,
620 &run, NULL)) ? 0 : 1;
621 while (NULL != (pos = cache_head))
623 GNUNET_CONTAINER_DLL_remove (cache_head,
626 GNUNET_free_non_null (pos->addr);
632 #if defined(LINUX) && defined(__GLIBC__)
636 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
638 void __attribute__ ((constructor))
639 GNUNET_ARM_memory_init ()
641 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
642 mallopt (M_TOP_PAD, 1 * 1024);
648 /* end of gnunet-service-resolver.c */