X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fdns%2Fdnsstub.c;h=f9dc7a6966c13dc773fb8f7ee406190551748df6;hb=081f818a024f52aca9de82fc2c8c1cc181603455;hp=687259feb6c56315d99249b3f8cb8b52310c1fa7;hpb=fa020943de61fe7177c9e65aca57b436305c6a14;p=oweals%2Fgnunet.git diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c index 687259feb..f9dc7a696 100644 --- a/src/dns/dnsstub.c +++ b/src/dns/dnsstub.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2012 Christian Grothoff (and other contributing authors) + Copyright (C) 2012 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** * @file dns/dnsstub.c @@ -24,6 +24,7 @@ */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_tun_lib.h" #include "gnunet_dnsstub_lib.h" /** @@ -31,6 +32,11 @@ */ #define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) +/** + * Timeout for retrying DNS queries. + */ +#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250) + /** * How many DNS sockets do we open at most at the same time? * (technical socket maximum is this number x2 for IPv4+IPv6) @@ -43,7 +49,7 @@ */ struct GNUNET_DNSSTUB_RequestSocket { - + /** * UDP socket we use for this request for IPv4 */ @@ -60,14 +66,19 @@ struct GNUNET_DNSSTUB_RequestSocket GNUNET_DNSSTUB_ResultCallback rc; /** - * Closure for 'rc'. + * Closure for @e rc. */ void *rc_cls; /** * Task for reading from dnsout4 and dnsout6. */ - GNUNET_SCHEDULER_TaskIdentifier read_task; + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * Task for retrying transmission of the query. + */ + struct GNUNET_SCHEDULER_Task *retry_task; /** * When should this request time out? @@ -80,35 +91,44 @@ struct GNUNET_DNSSTUB_RequestSocket struct sockaddr_storage addr; /** - * Number of bytes in 'addr'. + * Number of bytes in @e addr. */ socklen_t addrlen; + /** + * Query we sent to @e addr. + */ + void *request; + + /** + * Number of bytes in @a request. + */ + size_t request_len; + }; /** * Handle to the stub resolver. - */ + */ struct GNUNET_DNSSTUB_Context { /** - * Array of all open sockets for DNS requests. + * Array of all open sockets for DNS requests. */ struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX]; /** * IP address to use for the DNS server if we are a DNS exit service - * (for VPN via mesh); otherwise NULL. + * (for VPN via cadet); otherwise NULL. */ char *dns_exit; }; - /** - * We're done with a GNUNET_DNSSTUB_RequestSocket, close it for now. + * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now. * * @param rs request socket to clean up */ @@ -125,10 +145,20 @@ cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs) GNUNET_NETWORK_socket_close (rs->dnsout6); rs->dnsout6 = NULL; } - if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) + if (NULL != rs->read_task) { GNUNET_SCHEDULER_cancel (rs->read_task); - rs->read_task = GNUNET_SCHEDULER_NO_TASK; + rs->read_task = NULL; + } + if (NULL != rs->retry_task) + { + GNUNET_SCHEDULER_cancel (rs->retry_task); + rs->retry_task = NULL; + } + if (NULL != rs->request) + { + GNUNET_free (rs->request); + rs->request = NULL; } } @@ -137,8 +167,8 @@ cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs) * Open source port for sending DNS requests * * @param af AF_INET or AF_INET6 - * @return GNUNET_OK on success - */ + * @return #GNUNET_OK on success + */ static struct GNUNET_NETWORK_Handle * open_socket (int af) { @@ -168,10 +198,10 @@ open_socket (int af) } sa->sa_family = af; if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, - sa, + sa, alen)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not bind to any port: %s\n"), STRERROR (errno)); GNUNET_NETWORK_socket_close (ret); @@ -185,16 +215,14 @@ open_socket (int af) * Read a DNS response from the (unhindered) UDP-Socket * * @param cls socket to read from - * @param tc scheduler context (must be shutdown or read ready) */ static void -read_response (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); +read_response (void *cls); /** * Get a socket of the specified address family to send out a - * UDP DNS request to the Internet. + * UDP DNS request to the Internet. * * @param ctx the DNSSTUB context * @param af desired address family @@ -207,8 +235,22 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, struct GNUNET_DNSSTUB_RequestSocket *rs; struct GNUNET_NETWORK_FDSet *rset; - rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - DNS_SOCKET_MAX)]; + for (unsigned int i=0;i<256;i++) + { + rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + DNS_SOCKET_MAX)]; + if (NULL == rs->rc) + break; + } + if (NULL != rs->rc) + { + /* signal "failure" */ + rs->rc (rs->rc_cls, + rs, + NULL, + 0); + rs->rc = NULL; + } rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); switch (af) { @@ -222,40 +264,89 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, break; default: return NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) + } + if (NULL != rs->read_task) { GNUNET_SCHEDULER_cancel (rs->read_task); - rs->read_task = GNUNET_SCHEDULER_NO_TASK; + rs->read_task = NULL; + } + if (NULL != rs->retry_task) + { + GNUNET_SCHEDULER_cancel (rs->retry_task); + rs->retry_task = NULL; + } + if (NULL != rs->request) + { + GNUNET_free (rs->request); + rs->request = NULL; } if ( (NULL == rs->dnsout4) && (NULL == rs->dnsout6) ) return NULL; rset = GNUNET_NETWORK_fdset_create (); if (NULL != rs->dnsout4) - GNUNET_NETWORK_fdset_set (rset, rs->dnsout4); + GNUNET_NETWORK_fdset_set (rset, + rs->dnsout4); if (NULL != rs->dnsout6) - GNUNET_NETWORK_fdset_set (rset, rs->dnsout6); + GNUNET_NETWORK_fdset_set (rset, + rs->dnsout6); rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, REQUEST_TIMEOUT, rset, NULL, - &read_response, rs); + &read_response, + rs); GNUNET_NETWORK_fdset_destroy (rset); return rs; } +/** + * Task to (re)transmit the DNS query, possibly repeatedly until + * we succeed. + * + * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *` + */ +static void +transmit_query (void *cls) +{ + struct GNUNET_DNSSTUB_RequestSocket *rs = cls; + struct GNUNET_NETWORK_Handle *ret; + + rs->retry_task = NULL; + ret = (NULL != rs->dnsout4) ? rs->dnsout4 : rs->dnsout6; + GNUNET_assert (NULL != ret); + if (GNUNET_SYSERR == + GNUNET_NETWORK_socket_sendto (ret, + rs->request, + rs->request_len, + (struct sockaddr *) &rs->addr, + rs->addrlen)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to send DNS request to %s\n"), + GNUNET_a2s ((struct sockaddr *) &rs->addr, + rs->addrlen)); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Sent DNS request to %s\n"), + GNUNET_a2s ((struct sockaddr *) &rs->addr, + rs->addrlen)); + rs->retry_task = GNUNET_SCHEDULER_add_delayed (DNS_RETRANSMIT_DELAY, + &transmit_query, + rs); +} + + /** * Perform DNS resolution. * * @param ctx stub resolver to use * @param sa the socket address - * @param sa_len the socket length + * @param sa_len the length of @a sa * @param request DNS request to transmit - * @param request_len number of bytes in msg + * @param request_len number of bytes in @a request * @param rc function to call with result - * @param rc_cls closure for 'rc' + * @param rc_cls closure for @a rc * @return socket used for the request, NULL on error */ struct GNUNET_DNSSTUB_RequestSocket * @@ -268,28 +359,22 @@ GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx, void *rc_cls) { struct GNUNET_DNSSTUB_RequestSocket *rs; - struct GNUNET_NETWORK_Handle *ret; - int af; - af = sa->sa_family; - if (NULL == (rs = get_request_socket (ctx, af))) + if (NULL == (rs = get_request_socket (ctx, + sa->sa_family))) return NULL; - if (NULL != rs->dnsout4) - ret = rs->dnsout4; - else - ret = rs->dnsout6; - GNUNET_assert (NULL != ret); + GNUNET_assert (NULL == rs->rc); + GNUNET_memcpy (&rs->addr, + sa, + sa_len); + rs->addrlen = sa_len; rs->rc = rc; rs->rc_cls = rc_cls; - if (GNUNET_SYSERR == - GNUNET_NETWORK_socket_sendto (ret, - request, - request_len, - sa, - sa_len)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Failed to send DNS request to %s\n"), - GNUNET_a2s (sa, sa_len)); + rs->request = GNUNET_memdup (request, + request_len); + rs->request_len = request_len; + rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, + rs); return rs; } @@ -321,7 +406,9 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, memset (&v4, 0, sizeof (v4)); memset (&v6, 0, sizeof (v6)); - if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr)) + if (1 == inet_pton (AF_INET, + ctx->dns_exit, + &v4.sin_addr)) { salen = sizeof (v4); v4.sin_family = AF_INET; @@ -332,7 +419,9 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, sa = (struct sockaddr *) &v4; af = AF_INET; } - else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr)) + else if (1 == inet_pton (AF_INET6, + ctx->dns_exit, + &v6.sin6_addr)) { salen = sizeof (v6); v6.sin6_family = AF_INET6; @@ -342,14 +431,16 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, #endif sa = (struct sockaddr *) &v6; af = AF_INET6; - } + } else { GNUNET_break (0); return NULL; } - if (NULL == (rs = get_request_socket (ctx, af))) + if (NULL == (rs = get_request_socket (ctx, + af))) return NULL; + GNUNET_assert (NULL == rs->rc); if (NULL != rs->dnsout4) dnsout = rs->dnsout4; else @@ -361,23 +452,23 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, ctx->dns_exit); return NULL; } - memcpy (&rs->addr, - sa, - salen); + GNUNET_memcpy (&rs->addr, + sa, + salen); rs->addrlen = salen; rs->rc = rc; rs->rc_cls = rc_cls; if (GNUNET_SYSERR == GNUNET_NETWORK_socket_sendto (dnsout, request, - request_len, sa, salen)) + request_len, + sa, + salen)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to send DNS request to %s\n"), GNUNET_a2s (sa, salen)); rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); - return rs; - } @@ -387,7 +478,7 @@ GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, * * @param rs request socket with callback details * @param dnsout socket to read from - * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket) + * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket) */ static int do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, @@ -400,7 +491,9 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, int len; #ifndef MINGW - if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) + if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), + FIONREAD, + &len)) { /* conservative choice: */ len = UINT16_MAX; @@ -409,41 +502,51 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, /* port the code above? */ len = UINT16_MAX; #endif - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving %d byte DNS reply\n", + len); { unsigned char buf[len] GNUNET_ALIGN; addrlen = sizeof (addr); - memset (&addr, 0, sizeof (addr)); - r = GNUNET_NETWORK_socket_recvfrom (dnsout, - buf, sizeof (buf), - (struct sockaddr*) &addr, &addrlen); + memset (&addr, 0, sizeof (addr)); + r = GNUNET_NETWORK_socket_recvfrom (dnsout, + buf, + sizeof (buf), + (struct sockaddr*) &addr, + &addrlen); if (-1 == r) { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom"); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "recvfrom"); GNUNET_NETWORK_socket_close (dnsout); return GNUNET_SYSERR; } if (sizeof (struct GNUNET_TUN_DnsHeader) > r) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Received DNS response that is too small (%u bytes)"), - r); + (unsigned int) r); return GNUNET_NO; } dns = (struct GNUNET_TUN_DnsHeader *) buf; if ( (addrlen != rs->addrlen) || - (0 != memcmp (&rs->addr, - &addr, - addrlen)) || - (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value) ) + (GNUNET_YES != + GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr, + (struct sockaddr *) &addr, + GNUNET_YES)) || + (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Request timeout or invalid sender address; ignoring reply\n"); return GNUNET_NO; + } if (NULL != rs->rc) rs->rc (rs->rc_cls, rs, dns, r); - } + } return GNUNET_OK; } @@ -452,43 +555,59 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, * Read a DNS response from the (unhindered) UDP-Socket * * @param cls socket to read from - * @param tc scheduler context (must be shutdown or read ready) */ static void -read_response (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +read_response (void *cls) { struct GNUNET_DNSSTUB_RequestSocket *rs = cls; struct GNUNET_NETWORK_FDSet *rset; + const struct GNUNET_SCHEDULER_TaskContext *tc; - rs->read_task = GNUNET_SCHEDULER_NO_TASK; + rs->read_task = NULL; + tc = GNUNET_SCHEDULER_get_task_context (); if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) { - /* timeout or shutdown */ + /* signal "failure" (from timeout) */ + if (NULL != rs->rc) + { + rs->rc (rs->rc_cls, + rs, + NULL, + 0); + rs->rc = NULL; + } + /* timeout */ cleanup_rs (rs); return; } /* read and process ready sockets */ if ((NULL != rs->dnsout4) && - (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) && - (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4))) + (GNUNET_NETWORK_fdset_isset (tc->read_ready, + rs->dnsout4)) && + (GNUNET_SYSERR == do_dns_read (rs, + rs->dnsout4))) rs->dnsout4 = NULL; if ((NULL != rs->dnsout6) && - (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) && - (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6))) + (GNUNET_NETWORK_fdset_isset (tc->read_ready, + rs->dnsout6)) && + (GNUNET_SYSERR == do_dns_read (rs, + rs->dnsout6))) rs->dnsout6 = NULL; /* re-schedule read task */ rset = GNUNET_NETWORK_fdset_create (); if (NULL != rs->dnsout4) - GNUNET_NETWORK_fdset_set (rset, rs->dnsout4); + GNUNET_NETWORK_fdset_set (rset, + rs->dnsout4); if (NULL != rs->dnsout6) - GNUNET_NETWORK_fdset_set (rset, rs->dnsout6); + GNUNET_NETWORK_fdset_set (rset, + rs->dnsout6); rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_TIME_absolute_get_remaining (rs->timeout), rset, NULL, - &read_response, rs); + &read_response, + rs); GNUNET_NETWORK_fdset_destroy (rset); } @@ -515,8 +634,8 @@ struct GNUNET_DNSSTUB_Context * GNUNET_DNSSTUB_start (const char *dns_ip) { struct GNUNET_DNSSTUB_Context *ctx; - - ctx = GNUNET_malloc (sizeof (struct GNUNET_DNSSTUB_Context)); + + ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context); if (NULL != dns_ip) ctx->dns_exit = GNUNET_strdup (dns_ip); return ctx;