X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fgnunet-service-resolver.c;h=c8d51077047ff5abedc9284606d71dabb6ab9f0a;hb=4a25afcbc1cd57637f340a5b5369b999429b79f9;hp=b0e2f0be4f76e883e398b4f7d2886c7c0106b38a;hpb=71f77f1be3d6e2da1b88fe4a4fdd44c3c69b5325;p=oweals%2Fgnunet.git diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c index b0e2f0be4..c8d510770 100644 --- a/src/util/gnunet-service-resolver.c +++ b/src/util/gnunet-service-resolver.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + (C) 2007-2013 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -23,16 +23,10 @@ * @brief code to do DNS resolution * @author Christian Grothoff */ - -#include #include "platform.h" -#include "gnunet_disk_lib.h" -#include "gnunet_getopt_lib.h" +#include "gnunet_util_lib.h" #include "gnunet_protocols.h" -#include "gnunet_service_lib.h" #include "gnunet_statistics_service.h" -#include "gnunet_strings_lib.h" -#include "gnunet_time_lib.h" #include "resolver.h" /** @@ -41,19 +35,24 @@ struct IPCache { /** - * This is a linked list. + * This is a doubly linked list. */ struct IPCache *next; + /** + * This is a doubly linked list. + */ + struct IPCache *prev; + /** * Hostname in human-readable form. */ char *addr; /** - * Hostname in binary format. + * Binary IP address, allocated at the end of this struct. */ - struct sockaddr *sa; + const void *ip; /** * Last time this entry was updated. @@ -66,16 +65,26 @@ struct IPCache struct GNUNET_TIME_Absolute last_request; /** - * Number of bytes in sa. + * Number of bytes in ip. + */ + size_t ip_len; + + /** + * Address family of the IP. */ - socklen_t salen; + int af; }; /** * Start of the linked list of cached DNS lookup results. */ -static struct IPCache *head; +static struct IPCache *cache_head; + +/** + * Tail of the linked list of cached DNS lookup results. + */ +static struct IPCache *cache_tail; #if HAVE_GETNAMEINFO @@ -88,10 +97,42 @@ static void getnameinfo_resolve (struct IPCache *cache) { char hostname[256]; - - if (0 == getnameinfo (cache->sa, - cache->salen, - hostname, sizeof (hostname), NULL, 0, 0)) + const struct sockaddr *sa; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + size_t salen; + + switch (cache->af) + { + case AF_INET: + GNUNET_assert (cache->ip_len == sizeof (struct in_addr)); + sa = (const struct sockaddr*) &v4; + memset (&v4, 0, sizeof (v4)); + v4.sin_addr = * (const struct in_addr*) cache->ip; + v4.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = sizeof (v4); +#endif + salen = sizeof (v4); + break; + case AF_INET6: + GNUNET_assert (cache->ip_len == sizeof (struct in6_addr)); + sa = (const struct sockaddr*) &v6; + memset (&v6, 0, sizeof (v6)); + v6.sin6_addr = * (const struct in6_addr*) cache->ip; + v6.sin6_family = AF_INET6; +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = sizeof (v6); +#endif + salen = sizeof (v6); + break; + default: + GNUNET_assert (0); + } + + if (0 == + getnameinfo (sa, salen, hostname, sizeof (hostname), NULL, + 0, 0)) cache->addr = GNUNET_strdup (hostname); } #endif @@ -108,24 +149,15 @@ gethostbyaddr_resolve (struct IPCache *cache) { struct hostent *ent; - switch (cache->sa->sa_family) - { - case AF_INET: - ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr, - sizeof (struct in_addr), AF_INET); - break; - case AF_INET6: - ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr, - sizeof (struct in6_addr), AF_INET6); - break; - default: - ent = NULL; - } + ent = gethostbyaddr (cache->ip, + cache->ip_len, + cache->af); if (ent != NULL) cache->addr = GNUNET_strdup (ent->h_name); } #endif + /** * Resolve the given request using the available methods. * @@ -145,7 +177,6 @@ cache_resolve (struct IPCache *cache) } - /** * Get an IP address as a string (works for both IPv4 and IPv6). Note * that the resolution happens asynchronously and that the first call @@ -153,87 +184,82 @@ cache_resolve (struct IPCache *cache) * human-readable IP address). * * @param client handle to the client making the request (for sending the reply) - * @param sa should be of type "struct sockaddr*" - * @param salen number of bytes in sa + * @param af AF_INET or AF_INET6 + * @param ip 'struct in_addr' or 'struct in6_addr' */ static void get_ip_as_string (struct GNUNET_SERVER_Client *client, - const struct sockaddr *sa, socklen_t salen) + int af, + const void *ip) { - struct IPCache *cache; - struct IPCache *prev; + struct IPCache *pos; + struct IPCache *next; struct GNUNET_TIME_Absolute now; struct GNUNET_SERVER_TransmitContext *tc; - - if (salen < sizeof (struct sockaddr)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } + size_t ip_len; + + switch (af) + { + case AF_INET: + ip_len = sizeof (struct in_addr); + break; + case AF_INET6: + ip_len = sizeof (struct in6_addr); + break; + default: + GNUNET_assert (0); + } now = GNUNET_TIME_absolute_get (); - cache = head; - prev = NULL; - while ((cache != NULL) && - ((cache->salen != salen) || (0 != memcmp (cache->sa, sa, salen)))) + next = cache_head; + while ( (NULL != (pos = next)) && + ( (pos->af != af) || + (pos->ip_len != ip_len) || + (0 != memcmp (pos->ip, ip, ip_len))) ) + { + next = pos->next; + if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us < + 60 * 60 * 1000 * 1000LL) { - if (GNUNET_TIME_absolute_get_duration (cache->last_request).rel_value < - 60 * 60 * 1000) - { - if (prev != NULL) - { - prev->next = cache->next; - GNUNET_free_non_null (cache->addr); - GNUNET_free (cache->sa); - GNUNET_free (cache); - cache = prev->next; - } - else - { - head = cache->next; - GNUNET_free_non_null (cache->addr); - GNUNET_free (cache->sa); - GNUNET_free (cache); - cache = head; - } - continue; - } - prev = cache; - cache = cache->next; + GNUNET_CONTAINER_DLL_remove (cache_head, + cache_tail, + pos); + GNUNET_free_non_null (pos->addr); + GNUNET_free (pos); + continue; } - if (cache != NULL) + } + if (pos != NULL) + { + pos->last_request = now; + if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us < + 60 * 60 * 1000 * 1000LL) { - cache->last_request = now; - if (GNUNET_TIME_absolute_get_duration (cache->last_request).rel_value < - 60 * 60 * 1000) - { - GNUNET_free_non_null (cache->addr); - cache->addr = NULL; - cache->salen = 0; - cache_resolve (cache); - } + GNUNET_free_non_null (pos->addr); + pos->addr = NULL; + cache_resolve (pos); } + } else - { - cache = GNUNET_malloc (sizeof (struct IPCache)); - cache->next = head; - cache->salen = salen; - cache->sa = GNUNET_malloc (salen); - memcpy (cache->sa, sa, salen); - cache->last_request = GNUNET_TIME_absolute_get (); - cache->last_refresh = GNUNET_TIME_absolute_get (); - cache->addr = NULL; - cache_resolve (cache); - head = cache; - } + { + pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len); + pos->ip = &pos[1]; + memcpy (&pos[1], ip, ip_len); + pos->last_request = now; + pos->last_refresh = now; + pos->ip_len = ip_len; + pos->af = af; + GNUNET_CONTAINER_DLL_insert (cache_head, + cache_tail, + pos); + cache_resolve (pos); + } tc = GNUNET_SERVER_transmit_context_create (client); - if (cache->addr != NULL) - GNUNET_SERVER_transmit_context_append_data (tc, - cache->addr, - strlen (cache->addr) + 1, - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + if (pos->addr != NULL) + GNUNET_SERVER_transmit_context_append_data (tc, pos->addr, + strlen (pos->addr) + 1, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0, - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); } @@ -241,7 +267,7 @@ get_ip_as_string (struct GNUNET_SERVER_Client *client, #if HAVE_GETADDRINFO static int getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc, - const char *hostname, int domain) + const char *hostname, int af) { int s; struct addrinfo hints; @@ -251,132 +277,139 @@ getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc, memset (&hints, 0, sizeof (struct addrinfo)); // FIXME in PlibC #ifndef MINGW - hints.ai_family = domain; + hints.ai_family = af; #else hints.ai_family = AF_INET; #endif hints.ai_socktype = SOCK_STREAM; /* go for TCP */ if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result))) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Could not resolve `%s' (%s): %s\n"), hostname, - (domain == - AF_INET) ? "IPv4" : ((domain == - AF_INET6) ? "IPv6" : "any"), - gai_strerror (s)); - if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) || + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Could not resolve `%s' (%s): %s\n"), + hostname, + (af == + AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"), + gai_strerror (s)); + if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) #ifndef MINGW - (s == EAI_SYSTEM) + || (s == EAI_SYSTEM) #else - // FIXME NILS - 1 + // FIXME NILS + || 1 #endif ) - return GNUNET_NO; /* other function may still succeed */ - return GNUNET_SYSERR; - } + return GNUNET_NO; /* other function may still succeed */ + return GNUNET_SYSERR; + } if (result == NULL) return GNUNET_SYSERR; pos = result; while (pos != NULL) + { + switch (pos->ai_family) { + case AF_INET: GNUNET_SERVER_transmit_context_append_data (tc, - pos->ai_addr, - pos->ai_addrlen, + &((struct sockaddr_in*) pos->ai_addr)->sin_addr, + sizeof (struct in_addr), GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); - pos = pos->ai_next; + break; + case AF_INET6: + GNUNET_SERVER_transmit_context_append_data (tc, + &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr, + sizeof (struct in6_addr), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + break; + default: + /* unsupported, skip */ + break; } + pos = pos->ai_next; + } freeaddrinfo (result); return GNUNET_OK; } #endif + #if HAVE_GETHOSTBYNAME2 static int gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc, - const char *hostname, int domain) + const char *hostname, int af) { struct hostent *hp; - struct sockaddr_in a4; - struct sockaddr_in6 a6; int ret1; int ret2; - if (domain == AF_UNSPEC) - { - ret1 = gethostbyname2_resolve (tc, hostname, AF_INET); - ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6); - if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK)) - return GNUNET_OK; - if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR)) - return GNUNET_SYSERR; - return GNUNET_NO; - } - hp = gethostbyname2 (hostname, domain); - if (hp == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Could not find IP of host `%s': %s\n"), - hostname, hstrerror (h_errno)); + if (af == AF_UNSPEC) + { + ret1 = gethostbyname2_resolve (tc, hostname, AF_INET); + ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6); + if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK)) + return GNUNET_OK; + if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR)) return GNUNET_SYSERR; - } - GNUNET_assert (hp->h_addrtype == domain); - if (domain == AF_INET) - { - GNUNET_assert (hp->h_length == sizeof (struct in_addr)); - memset (&a4, 0, sizeof (a4)); - a4.sin_family = AF_INET; - memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length); - GNUNET_SERVER_transmit_context_append_data (tc, - &a4, - sizeof (a4), - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); - } - else - { - GNUNET_assert (hp->h_length == sizeof (struct in6_addr)); - memset (&a6, 0, sizeof (a6)); - a6.sin6_family = AF_INET6; - memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length); - GNUNET_SERVER_transmit_context_append_data (tc, - &a6, - sizeof (a6), - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); - } + return GNUNET_NO; + } + hp = gethostbyname2 (hostname, af); + if (hp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), hostname, + hstrerror (h_errno)); + return GNUNET_SYSERR; + } + GNUNET_assert (hp->h_addrtype == af); + switch (af) + { + case AF_INET: + GNUNET_assert (hp->h_length == sizeof (struct in_addr)); + GNUNET_SERVER_transmit_context_append_data (tc, + hp->h_addr_list[0], + hp->h_length, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + break; + case AF_INET6: + GNUNET_assert (hp->h_length == sizeof (struct in6_addr)); + GNUNET_SERVER_transmit_context_append_data (tc, + hp->h_addr_list[0], + hp->h_length, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + break; + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } return GNUNET_OK; } #endif + #if HAVE_GETHOSTBYNAME static int gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc, const char *hostname) { struct hostent *hp; - struct sockaddr_in addr; hp = GETHOSTBYNAME (hostname); if (hp == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Could not find IP of host `%s': %s\n"), - hostname, hstrerror (h_errno)); - return GNUNET_SYSERR; - } + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), hostname, + hstrerror (h_errno)); + return GNUNET_SYSERR; + } if (hp->h_addrtype != AF_INET) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + { + GNUNET_break (0); + return GNUNET_SYSERR; + } GNUNET_assert (hp->h_length == sizeof (struct in_addr)); - memset (&addr, 0, sizeof (addr)); - addr.sin_family = AF_INET; - memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length); GNUNET_SERVER_transmit_context_append_data (tc, - &addr, - sizeof (addr), - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + hp->h_addr_list[0], + hp->h_length, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); return GNUNET_OK; } #endif @@ -387,11 +420,11 @@ gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc, * * @param client where to send the IP address * @param hostname the hostname to resolve - * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any" */ static void -get_ip_from_hostname (struct GNUNET_SERVER_Client *client, - const char *hostname, int domain) +get_ip_from_hostname (struct GNUNET_SERVER_Client *client, const char *hostname, + int af) { int ret; struct GNUNET_SERVER_TransmitContext *tc; @@ -400,18 +433,18 @@ get_ip_from_hostname (struct GNUNET_SERVER_Client *client, ret = GNUNET_NO; #if HAVE_GETADDRINFO if (ret == GNUNET_NO) - ret = getaddrinfo_resolve (tc, hostname, domain); + ret = getaddrinfo_resolve (tc, hostname, af); #endif #if HAVE_GETHOSTBYNAME2 if (ret == GNUNET_NO) - ret = gethostbyname2_resolve (tc, hostname, domain); + ret = gethostbyname2_resolve (tc, hostname, af); #endif #if HAVE_GETHOSTBYNAME - if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET))) + if ((ret == GNUNET_NO) && ((af == AF_UNSPEC) || (af == PF_INET))) gethostbyname_resolve (tc, hostname); #endif GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0, - GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); } @@ -424,52 +457,76 @@ get_ip_from_hostname (struct GNUNET_SERVER_Client *client, * @param message the actual message */ static void -handle_get (void *cls, - struct GNUNET_SERVER_Client *client, +handle_get (void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { uint16_t msize; const struct GNUNET_RESOLVER_GetMessage *msg; - const char *hostname; + const void *ip; uint16_t size; int direction; - int domain; + int af; msize = ntohs (message->size); if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_RESOLVER_GetMessage *) message; + size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage); + direction = ntohl (msg->direction); + af = ntohl (msg->af); + if (direction == GNUNET_NO) + { + /* IP from hostname */ + const char *hostname; + + hostname = (const char *) &msg[1]; + if (hostname[size - 1] != '\0') { GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - msg = (const struct GNUNET_RESOLVER_GetMessage *) message; - size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage); - direction = ntohl (msg->direction); - domain = ntohl (msg->domain); - if (direction == GNUNET_NO) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resolver asked to look up `%s'.\n", + hostname); + get_ip_from_hostname (client, hostname, af); + return; + } + ip = &msg[1]; + switch (af) + { + case AF_INET: + if (size != sizeof (struct in_addr)) { - /* IP from hostname */ - hostname = (const char *) &msg[1]; - if (hostname[size - 1] != '\0') - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } -#if DEBUG_RESOLVER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Resolver asked to look up `%s'.\n"), hostname); -#endif - get_ip_from_hostname (client, hostname, domain); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; } - else + break; + case AF_INET6: + if (size != sizeof (struct in6_addr)) { -#if DEBUG_RESOLVER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Resolver asked to look up IP address.\n")); -#endif - get_ip_as_string (client, (const struct sockaddr *) &msg[1], size); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; } + break; + default: + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + { + char buf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolver asked to look up IP address `%s'.\n", + inet_ntop (af, ip, buf, sizeof (buf))); + } + get_ip_as_string (client, af, ip); } @@ -477,14 +534,11 @@ handle_get (void *cls, * Process resolver requests. * * @param cls closure - * @param sched scheduler to use * @param server the initialized server * @param cfg configuration to use */ static void -run (void *cls, - struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_SERVER_Handle *server, +run (void *cls, struct GNUNET_SERVER_Handle *server, const struct GNUNET_CONFIGURATION_Handle *cfg) { static const struct GNUNET_SERVER_MessageHandler handlers[] = { @@ -505,24 +559,37 @@ run (void *cls, int main (int argc, char *const *argv) { - int ret; struct IPCache *pos; + int ret; - ret = (GNUNET_OK == - GNUNET_SERVICE_run (argc, - argv, - "resolver", GNUNET_SERVICE_OPTION_NONE, - &run, NULL)) ? 0 : 1; - - while (head != NULL) - { - pos = head->next; - GNUNET_free_non_null (head->addr); - GNUNET_free (head->sa); - GNUNET_free (head); - head = pos; - } + ret = + (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "resolver", GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; + while (NULL != (pos = cache_head)) + { + GNUNET_CONTAINER_DLL_remove (cache_head, + cache_tail, + pos); + GNUNET_free_non_null (pos->addr); + GNUNET_free (pos); + } return ret; } +#ifdef LINUX +#include + +/** + * MINIMIZE heap size (way below 128k) since this process doesn't need much. + */ +void __attribute__ ((constructor)) GNUNET_ARM_memory_init () +{ + mallopt (M_TRIM_THRESHOLD, 4 * 1024); + mallopt (M_TOP_PAD, 1 * 1024); + malloc_trim (0); +} +#endif + + /* end of gnunet-service-resolver.c */