2 This file is part of GNUnet.
3 Copyright (C) 2007-2016 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 * Function called after the replies for the request have all
201 * been transmitted to the client, and we can now read the next
202 * request from the client.
204 * @param cls the `struct GNUNET_SERVICE_Client` to continue with
207 notify_service_client_done (void *cls)
209 struct GNUNET_SERVICE_Client *client = cls;
211 GNUNET_SERVICE_client_continue (client);
216 * Get an IP address as a string (works for both IPv4 and IPv6). Note
217 * that the resolution happens asynchronously and that the first call
218 * may not immediately result in the FQN (but instead in a
219 * human-readable IP address).
221 * @param client handle to the client making the request (for sending the reply)
222 * @param af AF_INET or AF_INET6
223 * @param ip `struct in_addr` or `struct in6_addr`
226 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
231 struct IPCache *next;
232 struct GNUNET_TIME_Absolute now;
233 struct GNUNET_MQ_Envelope *env;
234 struct GNUNET_MQ_Handle *mq;
235 struct GNUNET_MessageHeader *msg;
243 ip_len = sizeof (struct in_addr);
246 ip_len = sizeof (struct in6_addr);
251 now = GNUNET_TIME_absolute_get ();
253 while ( (NULL != (pos = next)) &&
255 (pos->ip_len != ip_len) ||
256 (0 != memcmp (pos->ip, ip, ip_len))) )
259 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
260 60 * 60 * 1000 * 1000LL)
262 GNUNET_CONTAINER_DLL_remove (cache_head,
265 GNUNET_free_non_null (pos->addr);
272 if ( (1 == inet_pton (af,
275 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
276 120 * 1000 * 1000LL) )
278 /* try again if still numeric AND 2 minutes have expired */
279 GNUNET_free_non_null (pos->addr);
282 pos->last_request = now;
287 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
289 GNUNET_memcpy (&pos[1],
292 pos->last_request = now;
293 pos->last_refresh = now;
294 pos->ip_len = ip_len;
296 GNUNET_CONTAINER_DLL_insert (cache_head,
301 if (NULL != pos->addr)
302 alen = strlen (pos->addr) + 1;
305 mq = GNUNET_SERVICE_client_get_mq (client);
306 env = GNUNET_MQ_msg_extra (msg,
308 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
309 GNUNET_memcpy (&msg[1],
314 env = GNUNET_MQ_msg (msg,
315 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
316 GNUNET_MQ_notify_sent (env,
317 ¬ify_service_client_done,
326 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
327 const char *hostname,
331 struct addrinfo hints;
332 struct addrinfo *result;
333 struct addrinfo *pos;
334 struct GNUNET_MessageHeader *msg;
335 struct GNUNET_MQ_Envelope *env;
338 /* Due to a bug, getaddrinfo will not return a mix of different families */
343 ret1 = getaddrinfo_resolve (mq,
346 ret2 = getaddrinfo_resolve (mq,
349 if ( (ret1 == GNUNET_OK) ||
350 (ret2 == GNUNET_OK) )
352 if ( (ret1 == GNUNET_SYSERR) ||
353 (ret2 == GNUNET_SYSERR) )
354 return GNUNET_SYSERR;
361 sizeof (struct addrinfo));
362 hints.ai_family = af;
363 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
365 if (0 != (s = getaddrinfo (hostname,
370 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
371 _("Could not resolve `%s' (%s): %s\n"),
374 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
376 if ( (s == EAI_BADFLAGS) ||
381 return GNUNET_NO; /* other function may still succeed */
382 return GNUNET_SYSERR;
385 return GNUNET_SYSERR;
386 for (pos = result; pos != NULL; pos = pos->ai_next)
388 switch (pos->ai_family)
391 env = GNUNET_MQ_msg_extra (msg,
392 sizeof (struct in_addr),
393 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
394 GNUNET_memcpy (&msg[1],
395 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
396 sizeof (struct in_addr));
401 env = GNUNET_MQ_msg_extra (msg,
402 sizeof (struct in6_addr),
403 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
404 GNUNET_memcpy (&msg[1],
405 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
406 sizeof (struct in6_addr));
411 /* unsupported, skip */
415 freeaddrinfo (result);
420 #elif HAVE_GETHOSTBYNAME2
424 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
425 const char *hostname,
431 struct GNUNET_MQ_Envelope *env;
432 struct GNUNET_MessageHeader *msg;
435 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
441 ret1 = gethostbyname2_resolve (mq,
444 ret2 = gethostbyname2_resolve (mq,
447 if ( (ret1 == GNUNET_OK) ||
448 (ret2 == GNUNET_OK) )
450 if ( (ret1 == GNUNET_SYSERR) ||
451 (ret2 == GNUNET_SYSERR) )
452 return GNUNET_SYSERR;
455 hp = gethostbyname2 (hostname,
459 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
460 _("Could not find IP of host `%s': %s\n"),
462 hstrerror (h_errno));
463 return GNUNET_SYSERR;
465 GNUNET_assert (hp->h_addrtype == af);
469 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
470 env = GNUNET_MQ_msg_extra (msg,
472 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
473 GNUNET_memcpy (&msg[1],
480 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
481 env = GNUNET_MQ_msg_extra (msg,
483 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
484 GNUNET_memcpy (&msg[1],
492 return GNUNET_SYSERR;
497 #elif HAVE_GETHOSTBYNAME
501 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
502 const char *hostname)
505 struct GNUNET_MessageHeader *msg;
506 struct GNUNET_MQ_Envelope *env;
508 hp = GETHOSTBYNAME (hostname);
511 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
512 _("Could not find IP of host `%s': %s\n"),
514 hstrerror (h_errno));
515 return GNUNET_SYSERR;
517 if (hp->h_addrtype != AF_INET)
520 return GNUNET_SYSERR;
522 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
523 env = GNUNET_MQ_msg_extra (msg,
525 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
526 GNUNET_memcpy (&msg[1],
537 * Convert a string to an IP address.
539 * @param client where to send the IP address
540 * @param hostname the hostname to resolve
541 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
544 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
545 const char *hostname,
549 struct GNUNET_MQ_Handle *mq;
550 struct GNUNET_MQ_Envelope *env;
551 struct GNUNET_MessageHeader *msg;
553 mq = GNUNET_SERVICE_client_get_mq (client);
556 if (ret == GNUNET_NO)
557 ret = getaddrinfo_resolve (mq,
560 #elif HAVE_GETHOSTBYNAME2
561 if (ret == GNUNET_NO)
562 ret = gethostbyname2_resolve (mq,
565 #elif HAVE_GETHOSTBYNAME
566 if ( (ret == GNUNET_NO) &&
567 ( (af == AF_UNSPEC) ||
569 gethostbyname_resolve (mq,
572 env = GNUNET_MQ_msg (msg,
573 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
574 GNUNET_MQ_notify_sent (env,
575 ¬ify_service_client_done,
583 * Verify well-formedness of GET-message.
586 * @param get the actual message
587 * @return #GNUNET_OK if @a get is well-formed
590 check_get (void *cls,
591 const struct GNUNET_RESOLVER_GetMessage *get)
597 size = ntohs (get->header.size) - sizeof (*get);
598 direction = ntohl (get->direction);
599 if (GNUNET_NO == direction)
601 /* IP from hostname */
602 const char *hostname;
604 hostname = (const char *) &get[1];
605 if (hostname[size - 1] != '\0')
608 return GNUNET_SYSERR;
612 af = ntohl (get->af);
616 if (size != sizeof (struct in_addr))
619 return GNUNET_SYSERR;
623 if (size != sizeof (struct in6_addr))
626 return GNUNET_SYSERR;
631 return GNUNET_SYSERR;
638 * Handle GET-message.
640 * @param cls identification of the client
641 * @param msg the actual message
644 handle_get (void *cls,
645 const struct GNUNET_RESOLVER_GetMessage *msg)
647 struct GNUNET_SERVICE_Client *client = cls;
652 direction = ntohl (msg->direction);
653 af = ntohl (msg->af);
654 if (GNUNET_NO == direction)
656 /* IP from hostname */
657 const char *hostname;
659 hostname = (const char *) &msg[1];
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Resolver asked to look up `%s'.\n",
663 get_ip_from_hostname (client,
670 char buf[INET6_ADDRSTRLEN];
672 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
673 "Resolver asked to look up IP address `%s'.\n",
679 get_ip_as_string (client,
686 * Callback called when a client connects to the service.
688 * @param cls closure for the service
689 * @param c the new client that connected to the service
690 * @param mq the message queue used to send messages to the client
694 connect_cb (void *cls,
695 struct GNUNET_SERVICE_Client *c,
696 struct GNUNET_MQ_Handle *mq)
703 * Callback called when a client disconnected from the service
705 * @param cls closure for the service
706 * @param c the client that disconnected
707 * @param internal_cls should be equal to @a c
710 disconnect_cb (void *cls,
711 struct GNUNET_SERVICE_Client *c,
714 GNUNET_assert (c == internal_cls);
719 * Define "main" method using service macro.
723 GNUNET_SERVICE_OPTION_NONE,
728 GNUNET_MQ_hd_var_size (get,
729 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
730 struct GNUNET_RESOLVER_GetMessage,
732 GNUNET_MQ_handler_end ());
735 #if defined(LINUX) && defined(__GLIBC__)
739 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
741 void __attribute__ ((constructor))
742 GNUNET_RESOLVER_memory_init ()
744 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
745 mallopt (M_TOP_PAD, 1 * 1024);
752 * Free globals on exit.
754 void __attribute__ ((destructor))
755 GNUNET_RESOLVER_memory_done ()
759 while (NULL != (pos = cache_head))
761 GNUNET_CONTAINER_DLL_remove (cache_head,
764 GNUNET_free_non_null (pos->addr);
770 /* end of gnunet-service-resolver.c */