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;
88 * Pipe for asynchronously notifying about resolve result
90 static struct GNUNET_DISK_PipeHandle *resolve_result_pipe;
93 * Task for reading from resolve_result_pipe
95 static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task;
100 * Resolve the given request using getnameinfo
102 * @param cache the request to resolve (and where to store the result)
105 getnameinfo_resolve (struct IPCache *cache)
108 const struct sockaddr *sa;
109 struct sockaddr_in v4;
110 struct sockaddr_in6 v6;
117 GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
118 sa = (const struct sockaddr*) &v4;
119 memset (&v4, 0, sizeof (v4));
120 v4.sin_addr = * (const struct in_addr*) cache->ip;
121 v4.sin_family = AF_INET;
122 #if HAVE_SOCKADDR_IN_SIN_LEN
123 v4.sin_len = sizeof (v4);
128 GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
129 sa = (const struct sockaddr*) &v6;
130 memset (&v6, 0, sizeof (v6));
131 v6.sin6_addr = * (const struct in6_addr*) cache->ip;
132 v6.sin6_family = AF_INET6;
133 #if HAVE_SOCKADDR_IN_SIN_LEN
134 v6.sin6_len = sizeof (v6);
143 (ret = getnameinfo (sa, salen,
144 hostname, sizeof (hostname),
148 cache->addr = GNUNET_strdup (hostname);
152 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
153 "getnameinfo failed: %s\n",
160 #if HAVE_GETHOSTBYADDR
162 * Resolve the given request using gethostbyaddr
164 * @param cache the request to resolve (and where to store the result)
167 gethostbyaddr_resolve (struct IPCache *cache)
171 ent = gethostbyaddr (cache->ip,
176 cache->addr = GNUNET_strdup (ent->h_name);
180 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
181 "gethostbyaddr failed: %s\n",
182 hstrerror (h_errno));
189 * Resolve the given request using the available methods.
191 * @param cache the request to resolve (and where to store the result)
194 cache_resolve (struct IPCache *cache)
197 if (NULL == cache->addr)
198 getnameinfo_resolve (cache);
200 #if HAVE_GETHOSTBYADDR
201 if (NULL == cache->addr)
202 gethostbyaddr_resolve (cache);
208 * Function called after the replies for the request have all
209 * been transmitted to the client, and we can now read the next
210 * request from the client.
212 * @param cls the `struct GNUNET_SERVICE_Client` to continue with
215 notify_service_client_done (void *cls)
217 struct GNUNET_SERVICE_Client *client = cls;
219 GNUNET_SERVICE_client_continue (client);
224 * Get an IP address as a string (works for both IPv4 and IPv6). Note
225 * that the resolution happens asynchronously and that the first call
226 * may not immediately result in the FQN (but instead in a
227 * human-readable IP address).
229 * @param client handle to the client making the request (for sending the reply)
230 * @param af AF_INET or AF_INET6
231 * @param ip `struct in_addr` or `struct in6_addr`
234 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
240 struct IPCache *next;
241 struct GNUNET_TIME_Absolute now;
242 struct GNUNET_MQ_Envelope *env;
243 struct GNUNET_MQ_Handle *mq;
244 struct GNUNET_RESOLVER_ResponseMessage *msg;
252 ip_len = sizeof (struct in_addr);
255 ip_len = sizeof (struct in6_addr);
260 now = GNUNET_TIME_absolute_get ();
262 while ( (NULL != (pos = next)) &&
264 (pos->ip_len != ip_len) ||
265 (0 != memcmp (pos->ip, ip, ip_len))) )
268 if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
269 60 * 60 * 1000 * 1000LL)
271 GNUNET_CONTAINER_DLL_remove (cache_head,
274 GNUNET_free_non_null (pos->addr);
281 if ( (1 == inet_pton (af,
284 (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
285 120 * 1000 * 1000LL) )
287 /* try again if still numeric AND 2 minutes have expired */
288 GNUNET_free_non_null (pos->addr);
291 pos->last_request = now;
296 pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
298 GNUNET_memcpy (&pos[1],
301 pos->last_request = now;
302 pos->last_refresh = now;
303 pos->ip_len = ip_len;
305 GNUNET_CONTAINER_DLL_insert (cache_head,
310 if (NULL != pos->addr)
311 alen = strlen (pos->addr) + 1;
314 mq = GNUNET_SERVICE_client_get_mq (client);
315 env = GNUNET_MQ_msg_extra (msg,
317 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
318 msg->id = request_id;
319 GNUNET_memcpy (&msg[1],
325 env = GNUNET_MQ_msg (msg,
326 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
327 msg->id = request_id;
328 GNUNET_MQ_notify_sent (env,
329 ¬ify_service_client_done,
336 #if HAVE_GETADDRINFO_A
340 struct sigevent *sig;
341 struct GNUNET_MQ_Handle *mq;
347 resolve_result_pipe_cb (void *cls)
349 struct AsyncCls *async_cls;
351 struct GNUNET_RESOLVER_ResponseMessage *msg;
352 struct GNUNET_MQ_Envelope *env;
354 GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (resolve_result_pipe,
355 GNUNET_DISK_PIPE_END_READ),
357 sizeof (struct AsyncCls *));
358 resolve_result_pipe_task =
359 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
360 GNUNET_DISK_pipe_handle (resolve_result_pipe,
361 GNUNET_DISK_PIPE_END_READ),
362 &resolve_result_pipe_cb,
364 host = async_cls->host;
365 for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next)
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Lookup result for hostname %s: %s (request ID %u)\n",
370 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen),
371 async_cls->request_id);
372 switch (pos->ai_family)
375 env = GNUNET_MQ_msg_extra (msg,
376 sizeof (struct in_addr),
377 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
378 msg->id = async_cls->request_id;
379 GNUNET_memcpy (&msg[1],
380 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
381 sizeof (struct in_addr));
382 GNUNET_MQ_send (async_cls->mq,
386 env = GNUNET_MQ_msg_extra (msg,
387 sizeof (struct in6_addr),
388 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
389 msg->id = async_cls->request_id;
390 GNUNET_memcpy (&msg[1],
391 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
392 sizeof (struct in6_addr));
393 GNUNET_MQ_send (async_cls->mq,
397 /* unsupported, skip */
402 env = GNUNET_MQ_msg (msg,
403 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
404 msg->id = async_cls->request_id;
405 GNUNET_MQ_send (async_cls->mq,
407 freeaddrinfo (host->ar_result);
408 GNUNET_free ((struct gaicb *)host->ar_request); // free hints
410 GNUNET_free (async_cls->sig);
411 GNUNET_free (async_cls);
416 handle_async_result (union sigval val)
418 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (resolve_result_pipe,
419 GNUNET_DISK_PIPE_END_WRITE),
421 sizeof (val.sival_ptr));
426 getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq,
427 const char *hostname,
433 struct addrinfo *hints;
434 struct sigevent *sig;
435 struct AsyncCls *async_cls;
437 host = GNUNET_new (struct gaicb);
438 hints = GNUNET_new (struct addrinfo);
439 sig = GNUNET_new (struct sigevent);
440 async_cls = GNUNET_new (struct AsyncCls);
443 sizeof (struct addrinfo));
446 sizeof (struct sigevent));
447 hints->ai_family = af;
448 hints->ai_socktype = SOCK_STREAM; /* go for TCP */
449 host->ar_name = hostname;
450 host->ar_service = NULL;
451 host->ar_request = hints;
452 host->ar_result = NULL;
453 sig->sigev_notify = SIGEV_THREAD;
454 sig->sigev_value.sival_ptr = async_cls;
455 sig->sigev_notify_function = &handle_async_result;
456 async_cls->host = host;
457 async_cls->sig = sig;
459 async_cls->request_id = request_id;
460 ret = getaddrinfo_a (GAI_NOWAIT,
465 return GNUNET_SYSERR;
470 #elif HAVE_GETADDRINFO
472 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
473 const char *hostname,
478 struct addrinfo hints;
479 struct addrinfo *result;
480 struct addrinfo *pos;
481 struct GNUNET_RESOLVER_ResponseMessage *msg;
482 struct GNUNET_MQ_Envelope *env;
485 /* Due to a bug, getaddrinfo will not return a mix of different families */
490 ret1 = getaddrinfo_resolve (mq,
494 ret2 = getaddrinfo_resolve (mq,
498 if ( (ret1 == GNUNET_OK) ||
499 (ret2 == GNUNET_OK) )
501 if ( (ret1 == GNUNET_SYSERR) ||
502 (ret2 == GNUNET_SYSERR) )
503 return GNUNET_SYSERR;
510 sizeof (struct addrinfo));
511 hints.ai_family = af;
512 hints.ai_socktype = SOCK_STREAM; /* go for TCP */
514 if (0 != (s = getaddrinfo (hostname,
519 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520 _("Could not resolve `%s' (%s): %s\n"),
523 AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
525 if ( (s == EAI_BADFLAGS) ||
530 return GNUNET_NO; /* other function may still succeed */
531 return GNUNET_SYSERR;
534 return GNUNET_SYSERR;
535 for (pos = result; pos != NULL; pos = pos->ai_next)
537 switch (pos->ai_family)
540 env = GNUNET_MQ_msg_extra (msg,
541 sizeof (struct in_addr),
542 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
543 msg->id = request_id;
544 GNUNET_memcpy (&msg[1],
545 &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
546 sizeof (struct in_addr));
551 env = GNUNET_MQ_msg_extra (msg,
552 sizeof (struct in6_addr),
553 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
554 msg->id = request_id;
555 GNUNET_memcpy (&msg[1],
556 &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
557 sizeof (struct in6_addr));
562 /* unsupported, skip */
566 freeaddrinfo (result);
571 #elif HAVE_GETHOSTBYNAME2
575 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
576 const char *hostname,
583 struct GNUNET_MQ_Envelope *env;
584 struct GNUNET_RESOLVER_ResponseMessage *msg;
587 /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
593 ret1 = gethostbyname2_resolve (mq,
597 ret2 = gethostbyname2_resolve (mq,
601 if ( (ret1 == GNUNET_OK) ||
602 (ret2 == GNUNET_OK) )
604 if ( (ret1 == GNUNET_SYSERR) ||
605 (ret2 == GNUNET_SYSERR) )
606 return GNUNET_SYSERR;
609 hp = gethostbyname2 (hostname,
613 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
614 _("Could not find IP of host `%s': %s\n"),
616 hstrerror (h_errno));
617 return GNUNET_SYSERR;
619 GNUNET_assert (hp->h_addrtype == af);
623 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
624 env = GNUNET_MQ_msg_extra (msg,
626 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
627 msg->id = request_id;
628 GNUNET_memcpy (&msg[1],
635 GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
636 env = GNUNET_MQ_msg_extra (msg,
638 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
639 msg->id = request_id;
640 GNUNET_memcpy (&msg[1],
648 return GNUNET_SYSERR;
653 #elif HAVE_GETHOSTBYNAME
657 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
658 const char *hostname,
662 struct GNUNET_RESOLVER_ResponseMessage *msg;
663 struct GNUNET_MQ_Envelope *env;
665 hp = GETHOSTBYNAME (hostname);
668 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
669 _("Could not find IP of host `%s': %s\n"),
671 hstrerror (h_errno));
672 return GNUNET_SYSERR;
674 if (hp->h_addrtype != AF_INET)
677 return GNUNET_SYSERR;
679 GNUNET_assert (hp->h_length == sizeof (struct in_addr));
680 env = GNUNET_MQ_msg_extra (msg,
682 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
683 msg->id = request_id;
684 GNUNET_memcpy (&msg[1],
695 * Convert a string to an IP address.
697 * @param client where to send the IP address
698 * @param hostname the hostname to resolve
699 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
702 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
703 const char *hostname,
707 struct GNUNET_MQ_Envelope *env;
708 struct GNUNET_RESOLVER_ResponseMessage *msg;
709 struct GNUNET_MQ_Handle *mq;
711 mq = GNUNET_SERVICE_client_get_mq (client);
712 #if HAVE_GETADDRINFO_A
713 getaddrinfo_a_resolve (mq,
717 GNUNET_SERVICE_client_continue (client);
719 #elif HAVE_GETADDRINFO
720 getaddrinfo_resolve (mq,
724 #elif HAVE_GETHOSTBYNAME2
725 gethostbyname2_resolve (mq,
729 #elif HAVE_GETHOSTBYNAME
730 if ( ( (af == AF_UNSPEC) ||
732 gethostbyname_resolve (mq,
737 env = GNUNET_MQ_msg (msg,
738 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
739 msg->id = request_id;
740 GNUNET_MQ_notify_sent (env,
741 ¬ify_service_client_done,
749 * Verify well-formedness of GET-message.
751 * @param cls closure, unused
752 * @param get the actual message
753 * @return #GNUNET_OK if @a get is well-formed
756 check_get (void *cls,
757 const struct GNUNET_RESOLVER_GetMessage *get)
764 size = ntohs (get->header.size) - sizeof (*get);
765 direction = ntohl (get->direction);
766 if (GNUNET_NO == direction)
768 /* IP from hostname */
769 const char *hostname;
771 hostname = (const char *) &get[1];
772 if (hostname[size - 1] != '\0')
775 return GNUNET_SYSERR;
779 af = ntohl (get->af);
783 if (size != sizeof (struct in_addr))
786 return GNUNET_SYSERR;
790 if (size != sizeof (struct in6_addr))
793 return GNUNET_SYSERR;
798 return GNUNET_SYSERR;
805 * Handle GET-message.
807 * @param cls identification of the client
808 * @param msg the actual message
811 handle_get (void *cls,
812 const struct GNUNET_RESOLVER_GetMessage *msg)
814 struct GNUNET_SERVICE_Client *client = cls;
820 direction = ntohl (msg->direction);
821 af = ntohl (msg->af);
822 id = ntohl (msg->id);
823 if (GNUNET_NO == direction)
825 /* IP from hostname */
826 const char *hostname;
828 hostname = (const char *) &msg[1];
829 get_ip_from_hostname (client,
837 #if !defined(GNUNET_CULL_LOGGING)
839 char buf[INET6_ADDRSTRLEN];
841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
842 "Resolver asked to look up IP address `%s (request ID %u)'.\n",
850 get_ip_as_string (client,
858 * Callback called when a client connects to the service.
860 * @param cls closure for the service, unused
861 * @param c the new client that connected to the service
862 * @param mq the message queue used to send messages to the client
866 connect_cb (void *cls,
867 struct GNUNET_SERVICE_Client *c,
868 struct GNUNET_MQ_Handle *mq)
873 #if HAVE_GETADDRINFO_A
874 resolve_result_pipe = GNUNET_DISK_pipe (GNUNET_NO,
878 GNUNET_assert (NULL != resolve_result_pipe);
879 resolve_result_pipe_task =
880 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
881 GNUNET_DISK_pipe_handle (resolve_result_pipe,
882 GNUNET_DISK_PIPE_END_READ),
883 &resolve_result_pipe_cb,
891 * Callback called when a client disconnected from the service
893 * @param cls closure for the service
894 * @param c the client that disconnected
895 * @param internal_cls should be equal to @a c
898 disconnect_cb (void *cls,
899 struct GNUNET_SERVICE_Client *c,
904 #if HAVE_GETADDRINFO_A
905 if (NULL != resolve_result_pipe_task)
907 GNUNET_SCHEDULER_cancel (resolve_result_pipe_task);
908 resolve_result_pipe_task = NULL;
910 if (NULL != resolve_result_pipe)
912 GNUNET_DISK_pipe_close (resolve_result_pipe);
913 resolve_result_pipe = NULL;
916 GNUNET_assert (c == internal_cls);
921 * Define "main" method using service macro.
925 GNUNET_SERVICE_OPTION_NONE,
930 GNUNET_MQ_hd_var_size (get,
931 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
932 struct GNUNET_RESOLVER_GetMessage,
934 GNUNET_MQ_handler_end ());
937 #if defined(LINUX) && defined(__GLIBC__)
941 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
943 void __attribute__ ((constructor))
944 GNUNET_RESOLVER_memory_init ()
946 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
947 mallopt (M_TOP_PAD, 1 * 1024);
954 * Free globals on exit.
956 void __attribute__ ((destructor))
957 GNUNET_RESOLVER_memory_done ()
961 while (NULL != (pos = cache_head))
963 GNUNET_CONTAINER_DLL_remove (cache_head,
966 GNUNET_free_non_null (pos->addr);
972 /* end of gnunet-service-resolver.c */