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 it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file util/gnunet-service-resolver.c
21 * @brief code to do DNS resolution
22 * @author Christian Grothoff
25 #include "gnunet_util_lib.h"
26 #include "gnunet_protocols.h"
27 #include "gnunet_statistics_service.h"
31 * A cached DNS lookup result (for reverse lookup).
36 * This is a doubly linked list.
41 * This is a doubly linked list.
46 * Hostname in human-readable form.
51 * Binary IP address, allocated at the end of this struct.
56 * Last time this entry was updated.
58 struct GNUNET_TIME_Absolute last_refresh;
61 * Last time this entry was requested.
63 struct GNUNET_TIME_Absolute last_request;
66 * Number of bytes in ip.
71 * Address family of the IP.
78 * Start of the linked list of cached DNS lookup results.
80 static struct IPCache *cache_head;
83 * Tail of the linked list of cached DNS lookup results.
85 static struct IPCache *cache_tail;
90 * Resolve the given request using getnameinfo
92 * @param cache the request to resolve (and where to store the result)
95 getnameinfo_resolve (struct IPCache *cache)
98 const struct sockaddr *sa;
99 struct sockaddr_in v4;
100 struct sockaddr_in6 v6;
107 GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
108 sa = (const struct sockaddr*) &v4;
109 memset (&v4, 0, sizeof (v4));
110 v4.sin_addr = * (const struct in_addr*) cache->ip;
111 v4.sin_family = AF_INET;
112 #if HAVE_SOCKADDR_IN_SIN_LEN
113 v4.sin_len = sizeof (v4);
118 GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
119 sa = (const struct sockaddr*) &v6;
120 memset (&v6, 0, sizeof (v6));
121 v6.sin6_addr = * (const struct in6_addr*) cache->ip;
122 v6.sin6_family = AF_INET6;
123 #if HAVE_SOCKADDR_IN_SIN_LEN
124 v6.sin6_len = sizeof (v6);
133 (ret = getnameinfo (sa, salen,
134 hostname, sizeof (hostname),
138 cache->addr = GNUNET_strdup (hostname);
142 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
143 "getnameinfo failed: %s\n",
150 #if HAVE_GETHOSTBYADDR
152 * Resolve the given request using gethostbyaddr
154 * @param cache the request to resolve (and where to store the result)
157 gethostbyaddr_resolve (struct IPCache *cache)
161 ent = gethostbyaddr (cache->ip,
166 cache->addr = GNUNET_strdup (ent->h_name);
170 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
171 "gethostbyaddr failed: %s\n",
172 hstrerror (h_errno));
179 * Resolve the given request using the available methods.
181 * @param cache the request to resolve (and where to store the result)
184 cache_resolve (struct IPCache *cache)
187 if (NULL == cache->addr)
188 getnameinfo_resolve (cache);
190 #if HAVE_GETHOSTBYADDR
191 if (NULL == cache->addr)
192 gethostbyaddr_resolve (cache);
198 * Function called after the replies for the request have all
199 * been transmitted to the client, and we can now read the next
200 * request from the client.
202 * @param cls the `struct GNUNET_SERVICE_Client` to continue with
205 notify_service_client_done (void *cls)
207 struct GNUNET_SERVICE_Client *client = cls;
209 GNUNET_SERVICE_client_continue (client);
214 * Get an IP address as a string (works for both IPv4 and IPv6). Note
215 * that the resolution happens asynchronously and that the first call
216 * may not immediately result in the FQN (but instead in a
217 * human-readable IP address).
219 * @param client handle to the client making the request (for sending the reply)
220 * @param af AF_INET or AF_INET6
221 * @param ip `struct in_addr` or `struct in6_addr`
224 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
229 struct IPCache *next;
230 struct GNUNET_TIME_Absolute now;
231 struct GNUNET_MQ_Envelope *env;
232 struct GNUNET_MQ_Handle *mq;
233 struct GNUNET_MessageHeader *msg;
241 ip_len = sizeof (struct in_addr);
244 ip_len = sizeof (struct in6_addr);
249 now = GNUNET_TIME_absolute_get ();
251 while ( (NULL != (pos = next)) &&
253 (pos->ip_len != ip_len) ||
254 (0 != memcmp (pos->ip, ip, ip_len))) )
257 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
258 60 * 60 * 1000 * 1000LL)
260 GNUNET_CONTAINER_DLL_remove (cache_head,
263 GNUNET_free_non_null (pos->addr);
270 if ( (1 == inet_pton (af,
273 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
274 120 * 1000 * 1000LL) )
276 /* try again if still numeric AND 2 minutes have expired */
277 GNUNET_free_non_null (pos->addr);
280 pos->last_request = now;
285 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
287 GNUNET_memcpy (&pos[1],
290 pos->last_request = now;
291 pos->last_refresh = now;
292 pos->ip_len = ip_len;
294 GNUNET_CONTAINER_DLL_insert (cache_head,
299 if (NULL != pos->addr)
300 alen = strlen (pos->addr) + 1;
303 mq = GNUNET_SERVICE_client_get_mq (client);
304 env = GNUNET_MQ_msg_extra (msg,
306 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
307 GNUNET_memcpy (&msg[1],
312 env = GNUNET_MQ_msg (msg,
313 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
314 GNUNET_MQ_notify_sent (env,
315 ¬ify_service_client_done,
324 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
325 const char *hostname,
329 struct addrinfo hints;
330 struct addrinfo *result;
331 struct addrinfo *pos;
332 struct GNUNET_MessageHeader *msg;
333 struct GNUNET_MQ_Envelope *env;
336 /* Due to a bug, getaddrinfo will not return a mix of different families */
341 ret1 = getaddrinfo_resolve (mq,
344 ret2 = getaddrinfo_resolve (mq,
347 if ( (ret1 == GNUNET_OK) ||
348 (ret2 == GNUNET_OK) )
350 if ( (ret1 == GNUNET_SYSERR) ||
351 (ret2 == GNUNET_SYSERR) )
352 return GNUNET_SYSERR;
359 sizeof (struct addrinfo));
360 hints.ai_family = af;
361 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
363 if (0 != (s = getaddrinfo (hostname,
368 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
369 _("Could not resolve `%s' (%s): %s\n"),
372 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
374 if ( (s == EAI_BADFLAGS) ||
379 return GNUNET_NO; /* other function may still succeed */
380 return GNUNET_SYSERR;
383 return GNUNET_SYSERR;
384 for (pos = result; pos != NULL; pos = pos->ai_next)
386 switch (pos->ai_family)
389 env = GNUNET_MQ_msg_extra (msg,
390 sizeof (struct in_addr),
391 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
392 GNUNET_memcpy (&msg[1],
393 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
394 sizeof (struct in_addr));
399 env = GNUNET_MQ_msg_extra (msg,
400 sizeof (struct in6_addr),
401 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
402 GNUNET_memcpy (&msg[1],
403 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
404 sizeof (struct in6_addr));
409 /* unsupported, skip */
413 freeaddrinfo (result);
418 #elif HAVE_GETHOSTBYNAME2
422 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
423 const char *hostname,
429 struct GNUNET_MQ_Envelope *env;
430 struct GNUNET_MessageHeader *msg;
433 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
439 ret1 = gethostbyname2_resolve (mq,
442 ret2 = gethostbyname2_resolve (mq,
445 if ( (ret1 == GNUNET_OK) ||
446 (ret2 == GNUNET_OK) )
448 if ( (ret1 == GNUNET_SYSERR) ||
449 (ret2 == GNUNET_SYSERR) )
450 return GNUNET_SYSERR;
453 hp = gethostbyname2 (hostname,
457 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
458 _("Could not find IP of host `%s': %s\n"),
460 hstrerror (h_errno));
461 return GNUNET_SYSERR;
463 GNUNET_assert (hp->h_addrtype == af);
467 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
468 env = GNUNET_MQ_msg_extra (msg,
470 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
471 GNUNET_memcpy (&msg[1],
478 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
479 env = GNUNET_MQ_msg_extra (msg,
481 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
482 GNUNET_memcpy (&msg[1],
490 return GNUNET_SYSERR;
495 #elif HAVE_GETHOSTBYNAME
499 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
500 const char *hostname)
503 struct GNUNET_MessageHeader *msg;
504 struct GNUNET_MQ_Envelope *env;
506 hp = GETHOSTBYNAME (hostname);
509 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
510 _("Could not find IP of host `%s': %s\n"),
512 hstrerror (h_errno));
513 return GNUNET_SYSERR;
515 if (hp->h_addrtype != AF_INET)
518 return GNUNET_SYSERR;
520 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
521 env = GNUNET_MQ_msg_extra (msg,
523 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
524 GNUNET_memcpy (&msg[1],
535 * Convert a string to an IP address.
537 * @param client where to send the IP address
538 * @param hostname the hostname to resolve
539 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
542 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
543 const char *hostname,
547 struct GNUNET_MQ_Handle *mq;
548 struct GNUNET_MQ_Envelope *env;
549 struct GNUNET_MessageHeader *msg;
551 mq = GNUNET_SERVICE_client_get_mq (client);
554 if (ret == GNUNET_NO)
555 ret = getaddrinfo_resolve (mq,
558 #elif HAVE_GETHOSTBYNAME2
559 if (ret == GNUNET_NO)
560 ret = gethostbyname2_resolve (mq,
563 #elif HAVE_GETHOSTBYNAME
564 if ( (ret == GNUNET_NO) &&
565 ( (af == AF_UNSPEC) ||
567 gethostbyname_resolve (mq,
570 env = GNUNET_MQ_msg (msg,
571 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
572 GNUNET_MQ_notify_sent (env,
573 ¬ify_service_client_done,
581 * Verify well-formedness of GET-message.
583 * @param cls closure, unused
584 * @param get the actual message
585 * @return #GNUNET_OK if @a get is well-formed
588 check_get (void *cls,
589 const struct GNUNET_RESOLVER_GetMessage *get)
596 size = ntohs (get->header.size) - sizeof (*get);
597 direction = ntohl (get->direction);
598 if (GNUNET_NO == direction)
600 /* IP from hostname */
601 const char *hostname;
603 hostname = (const char *) &get[1];
604 if (hostname[size - 1] != '\0')
607 return GNUNET_SYSERR;
611 af = ntohl (get->af);
615 if (size != sizeof (struct in_addr))
618 return GNUNET_SYSERR;
622 if (size != sizeof (struct in6_addr))
625 return GNUNET_SYSERR;
630 return GNUNET_SYSERR;
637 * Handle GET-message.
639 * @param cls identification of the client
640 * @param msg the actual message
643 handle_get (void *cls,
644 const struct GNUNET_RESOLVER_GetMessage *msg)
646 struct GNUNET_SERVICE_Client *client = cls;
651 direction = ntohl (msg->direction);
652 af = ntohl (msg->af);
653 if (GNUNET_NO == direction)
655 /* IP from hostname */
656 const char *hostname;
658 hostname = (const char *) &msg[1];
659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660 "Resolver asked to look up `%s'.\n",
662 get_ip_from_hostname (client,
669 #if !defined(GNUNET_CULL_LOGGING)
671 char buf[INET6_ADDRSTRLEN];
673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674 "Resolver asked to look up IP address `%s'.\n",
681 get_ip_as_string (client,
688 * Callback called when a client connects to the service.
690 * @param cls closure for the service, unused
691 * @param c the new client that connected to the service
692 * @param mq the message queue used to send messages to the client
696 connect_cb (void *cls,
697 struct GNUNET_SERVICE_Client *c,
698 struct GNUNET_MQ_Handle *mq)
708 * Callback called when a client disconnected from the service
710 * @param cls closure for the service
711 * @param c the client that disconnected
712 * @param internal_cls should be equal to @a c
715 disconnect_cb (void *cls,
716 struct GNUNET_SERVICE_Client *c,
721 GNUNET_assert (c == internal_cls);
726 * Define "main" method using service macro.
730 GNUNET_SERVICE_OPTION_NONE,
735 GNUNET_MQ_hd_var_size (get,
736 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
737 struct GNUNET_RESOLVER_GetMessage,
739 GNUNET_MQ_handler_end ());
742 #if defined(LINUX) && defined(__GLIBC__)
746 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
748 void __attribute__ ((constructor))
749 GNUNET_RESOLVER_memory_init ()
751 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
752 mallopt (M_TOP_PAD, 1 * 1024);
759 * Free globals on exit.
761 void __attribute__ ((destructor))
762 GNUNET_RESOLVER_memory_done ()
766 while (NULL != (pos = cache_head))
768 GNUNET_CONTAINER_DLL_remove (cache_head,
771 GNUNET_free_non_null (pos->addr);
777 /* end of gnunet-service-resolver.c */