diff options
author | grothoff <grothoff@140774ce-b5e7-0310-ab8b-a85725594a96> | 2012-08-21 06:01:06 +0000 |
---|---|---|
committer | grothoff <grothoff@140774ce-b5e7-0310-ab8b-a85725594a96> | 2012-08-21 06:01:06 +0000 |
commit | fed37bbf3c6c469f197e84a57c39d9567fe58591 (patch) | |
tree | 71a692e8a19b10900dc2360173b1e8b06d1db70f /src/dns | |
parent | 3985e02f3518892aac5d1a90b71c6dd61e0ee4bd (diff) |
refactoring dns service to take stub code into separate library for use in gns2dns proxy
git-svn-id: https://gnunet.org/svn/gnunet@23331 140774ce-b5e7-0310-ab8b-a85725594a96
Diffstat (limited to 'src/dns')
-rw-r--r-- | src/dns/Makefile.am | 10 | ||||
-rw-r--r-- | src/dns/dnsstub.c | 520 | ||||
-rw-r--r-- | src/dns/gnunet-service-dns.c | 468 |
3 files changed, 606 insertions, 392 deletions
diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index 8a102c414b..aca8ea25cb 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am @@ -25,6 +25,7 @@ endif lib_LTLIBRARIES = \ libgnunetdnsparser.la \ + libgnunetdnsstub.la \ libgnunetdns.la bin_PROGRAMS = \ @@ -70,6 +71,7 @@ gnunet_dns_redirector_DEPENDENCIES = \ gnunet_service_dns_SOURCES = \ gnunet-service-dns.c gnunet_service_dns_LDADD = \ + $(top_builddir)/src/dns/libgnunetdnsstub.la \ $(top_builddir)/src/tun/libgnunettun.la \ $(top_builddir)/src/mesh/libgnunetmesh.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ @@ -84,6 +86,14 @@ libgnunetdnsparser_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) \ -version-info 0:0:0 +libgnunetdnsstub_la_SOURCES = \ + dnsstub.c +libgnunetdnsstub_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIB) +libgnunetdnsstub_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + libgnunetdns_la_SOURCES = \ dns_api.c dns.h libgnunetdns_la_LIBADD = \ diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c new file mode 100644 index 0000000000..383b1d6999 --- /dev/null +++ b/src/dns/dnsstub.c @@ -0,0 +1,520 @@ +/* + This file is part of GNUnet. + (C) 2012 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 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ +/** + * @file dns/dnsstub.c + * @brief DNS stub resolver which sends DNS requests to an actual resolver + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_dnsstub_lib.h" + +/** + * Timeout for an external (Internet-DNS) DNS resolution + */ +#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * How many DNS sockets do we open at most at the same time? + * (technical socket maximum is this number x2 for IPv4+IPv6) + */ +#define DNS_SOCKET_MAX 128 + + +/** + * UDP socket we are using for sending DNS requests to the Internet. + */ +struct GNUNET_DNSSTUB_RequestSocket +{ + + /** + * UDP socket we use for this request for IPv4 + */ + struct GNUNET_NETWORK_Handle *dnsout4; + + /** + * UDP socket we use for this request for IPv6 + */ + struct GNUNET_NETWORK_Handle *dnsout6; + + /** + * Function to call with result. + */ + GNUNET_DNSSTUB_ResultCallback rc; + + /** + * Closure for 'rc'. + */ + void *rc_cls; + + /** + * Task for reading from dnsout4 and dnsout6. + */ + GNUNET_SCHEDULER_TaskIdentifier read_task; + + /** + * When should this request time out? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Address we sent the DNS request to. + */ + struct sockaddr_storage addr; + + /** + * Number of bytes in 'addr'. + */ + socklen_t addrlen; + +}; + + +struct GNUNET_DNSSTUB_Context +{ + + /** + * 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. + */ + char *dns_exit; +}; + + + +/** + * We're done with a GNUNET_DNSSTUB_RequestSocket, close it for now. + * + * @param rs request socket to clean up + */ +static void +cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs) +{ + if (NULL != rs->dnsout4) + { + GNUNET_NETWORK_socket_close (rs->dnsout4); + rs->dnsout4 = NULL; + } + if (NULL != rs->dnsout6) + { + GNUNET_NETWORK_socket_close (rs->dnsout6); + rs->dnsout6 = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) + { + GNUNET_SCHEDULER_cancel (rs->read_task); + rs->read_task = GNUNET_SCHEDULER_NO_TASK; + } +} + + +/** + * Open source port for sending DNS requests + * + * @param af AF_INET or AF_INET6 + * @return GNUNET_OK on success + */ +static struct GNUNET_NETWORK_Handle * +open_socket (int af) +{ + struct sockaddr_in a4; + struct sockaddr_in6 a6; + struct sockaddr *sa; + socklen_t alen; + struct GNUNET_NETWORK_Handle *ret; + + ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0); + if (NULL == ret) + return NULL; + switch (af) + { + case AF_INET: + memset (&a4, 0, alen = sizeof (struct sockaddr_in)); + sa = (struct sockaddr *) &a4; + break; + case AF_INET6: + memset (&a6, 0, alen = sizeof (struct sockaddr_in6)); + sa = (struct sockaddr *) &a6; + break; + default: + GNUNET_break (0); + GNUNET_NETWORK_socket_close (ret); + return NULL; + } + sa->sa_family = af; + if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, + sa, + alen)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not bind to any port: %s\n"), + STRERROR (errno)); + GNUNET_NETWORK_socket_close (ret); + return NULL; + } + return ret; +} + + +/** + * 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); + + +/** + * Get a socket of the specified address family to send out a + * UDP DNS request to the Internet. + * + * @param af desired address family + * @return NULL on error (given AF not "supported") + */ +static struct GNUNET_DNSSTUB_RequestSocket * +get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, + int af) +{ + struct GNUNET_DNSSTUB_RequestSocket *rs; + struct GNUNET_NETWORK_FDSet *rset; + + rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + DNS_SOCKET_MAX)]; + rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); + switch (af) + { + case AF_INET: + if (NULL == rs->dnsout4) + rs->dnsout4 = open_socket (AF_INET); + break; + case AF_INET6: + if (NULL == rs->dnsout6) + rs->dnsout6 = open_socket (AF_INET6); + break; + default: + return NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) + { + GNUNET_SCHEDULER_cancel (rs->read_task); + rs->read_task = GNUNET_SCHEDULER_NO_TASK; + } + 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); + if (NULL != 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); + GNUNET_NETWORK_fdset_destroy (rset); + return rs; +} + + +/** + * Perform DNS resolution. + * + * @param ctx stub resolver to use + * @param af address family to use + * @param request DNS request to transmit + * @param request_len number of bytes in msg + * @param rc function to call with result + * @param rc_cls closure for 'rc' + * @return socket used for the request, NULL on error + */ +struct GNUNET_DNSSTUB_RequestSocket * +GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx, + const struct sockaddr *sa, + socklen_t sa_len, + const void *request, + size_t request_len, + GNUNET_DNSSTUB_ResultCallback rc, + 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))) + return NULL; + if (NULL != rs->dnsout4) + ret = rs->dnsout4; + else + ret = rs->dnsout6; + GNUNET_assert (NULL != ret); + rs->rc = rc; + rs->rc_cls = rc_cls; + GNUNET_NETWORK_socket_sendto (ret, + request, + request_len, + sa, + sa_len); + return rs; +} + + +/** + * Perform DNS resolution using our default IP from init. + * + * @param ctx stub resolver to use + * @param request DNS request to transmit + * @param request_len number of bytes in msg + * @param rc function to call with result + * @param rc_cls closure for 'rc' + * @return socket used for the request, NULL on error + */ +struct GNUNET_DNSSTUB_RequestSocket * +GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx, + const void *request, + size_t request_len, + GNUNET_DNSSTUB_ResultCallback rc, + void *rc_cls) +{ + int af; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + struct sockaddr *so; + socklen_t salen; + struct GNUNET_NETWORK_Handle *dnsout; + struct GNUNET_DNSSTUB_RequestSocket *rs; + + memset (&v4, 0, sizeof (v4)); + memset (&v6, 0, sizeof (v6)); + if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr)) + { + salen = sizeof (v4); + v4.sin_family = AF_INET; + v4.sin_port = htons (53); +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = (u_char) salen; +#endif + so = (struct sockaddr *) &v4; + af = AF_INET; + } + else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr)) + { + salen = sizeof (v6); + v6.sin6_family = AF_INET6; + v6.sin6_port = htons (53); +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = (u_char) salen; +#endif + so = (struct sockaddr *) &v6; + af = AF_INET6; + } + else + { + GNUNET_break (0); + return NULL; + } + if (NULL == (rs = get_request_socket (ctx, af))) + return NULL; + if (NULL != rs->dnsout4) + dnsout = rs->dnsout4; + else + dnsout = rs->dnsout6; + if (NULL == dnsout) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Configured DNS exit `%s' is not working / valid.\n"), + ctx->dns_exit); + return NULL; + } + memcpy (&rs->addr, + so, + salen); + rs->addrlen = salen; + GNUNET_NETWORK_socket_sendto (dnsout, + request, + request_len, so, salen); + rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); + + return rs; + +} + + +/** + * Actually do the reading of a DNS packet from our UDP socket and see + * if we have a valid, matching, pending request. + * + * @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) + */ +static int +do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, + struct GNUNET_NETWORK_Handle *dnsout) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + struct GNUNET_TUN_DnsHeader *dns; + ssize_t r; + int len; + +#ifndef MINGW + if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) + { + /* conservative choice: */ + len = UINT16_MAX; + } +#else + /* port the code above? */ + len = UINT16_MAX; +#endif + + { + 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); + if (-1 == r) + { + 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, + _("Received DNS response that is too small (%u bytes)"), + 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) ) + return GNUNET_NO; + rs->rc (rs->rc_cls, + rs, + dns, + r); + } + return GNUNET_OK; +} + + +/** + * 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) +{ + struct GNUNET_DNSSTUB_RequestSocket *rs = cls; + struct GNUNET_NETWORK_FDSet *rset; + + rs->read_task = GNUNET_SCHEDULER_NO_TASK; + if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) + { + /* timeout or shutdown */ + 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))) + rs->dnsout4 = NULL; + if ((NULL != 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); + if (NULL != 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); + GNUNET_NETWORK_fdset_destroy (rset); +} + + + +/** + * Start a DNS stub resolver. + * + * @param dns_ip target IP address to use + * @return NULL on error + */ +struct GNUNET_DNSSTUB_Context * +GNUNET_DNSSTUB_start (const char *dns_ip) +{ + struct GNUNET_DNSSTUB_Context *ctx; + + ctx = GNUNET_malloc (sizeof (struct GNUNET_DNSSTUB_Context)); + if (NULL != dns_ip) + ctx->dns_exit = GNUNET_strdup (dns_ip); + return ctx; +} + + +/** + * Cleanup DNSSTUB resolver. + * + * @param ctx stub resolver to clean up + */ +void +GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx) +{ + unsigned int i; + + for (i=0;i<=UINT16_MAX;i++) + cleanup_rs (&ctx->sockets[i]); + if (NULL != ctx->dns_exit) + { + GNUNET_free (ctx->dns_exit); + ctx->dns_exit = NULL; + } + GNUNET_free (ctx); +} + + +/* end of dnsstub.c */ diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c index 76ea1390f4..b13f156a9b 100644 --- a/src/dns/gnunet-service-dns.c +++ b/src/dns/gnunet-service-dns.c @@ -45,23 +45,13 @@ #include "dns.h" #include "gnunet_dns_service.h" #include "gnunet_dnsparser_lib.h" +#include "gnunet_dnsstub_lib.h" #include "gnunet_mesh_service.h" #include "gnunet_statistics_service.h" #include "gnunet_tun_lib.h" /** - * Timeout for an external (Internet-DNS) DNS resolution - */ -#define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - -/** - * How many DNS sockets do we open at most at the same time? - * (technical socket maximum is this number x2 for IPv4+IPv6) - */ -#define DNS_SOCKET_MAX 128 - -/** * Phases each request goes through. */ enum RequestPhase @@ -137,34 +127,6 @@ struct ClientRecord /** - * UDP socket we are using for sending DNS requests to the Internet. - */ -struct RequestSocket -{ - - /** - * UDP socket we use for this request for IPv4 - */ - struct GNUNET_NETWORK_Handle *dnsout4; - - /** - * UDP socket we use for this request for IPv6 - */ - struct GNUNET_NETWORK_Handle *dnsout6; - - /** - * Task for reading from dnsout4 and dnsout6. - */ - GNUNET_SCHEDULER_TaskIdentifier read_task; - - /** - * When should this socket be closed? - */ - struct GNUNET_TIME_Absolute timeout; -}; - - -/** * Entry we keep for each active request. */ struct RequestRecord @@ -184,10 +146,9 @@ struct RequestRecord /** * Socket we are using to transmit this request (must match if we receive - * a response). Must NOT be freed as part of this request record (as it - * might be shared with other requests). + * a response). */ - struct GNUNET_NETWORK_Handle *dnsout; + struct GNUNET_DNSSTUB_RequestSocket *rs; /** * Source address of the original request (for sending response). @@ -200,11 +161,6 @@ struct RequestRecord struct sockaddr_storage dst_addr; /** - * When should this request time out? - */ - struct GNUNET_TIME_Absolute timeout; - - /** * ID of this request, also basis for hashing. Lowest 16 bit will * be our message ID when doing a global DNS request and our index * into the 'requests' array. @@ -252,25 +208,9 @@ struct TunnelState /** * Socket we are using to transmit this request (must match if we receive - * a response). Must NOT be freed as part of this request record (as it - * might be shared with other requests). - */ - struct GNUNET_NETWORK_Handle *dnsout; - - /** - * Address we sent the DNS request to. - */ - struct sockaddr_storage addr; - - /** - * When should this request time out? + * a response). */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Number of bytes in 'addr'. - */ - socklen_t addrlen; + struct GNUNET_DNSSTUB_RequestSocket *rs; /** * Number of bytes in 'reply'. @@ -330,19 +270,14 @@ static struct ClientRecord *clients_tail; static struct GNUNET_SERVER_NotificationContext *nc; /** - * Array of all open requests. - */ -static struct RequestRecord requests[UINT16_MAX + 1]; - -/** * Array of all open requests from tunnels. */ static struct TunnelState *tunnels[UINT16_MAX + 1]; /** - * Array of all open sockets for DNS requests. + * Array of all open requests. */ -static struct RequestSocket sockets[DNS_SOCKET_MAX]; +static struct RequestRecord requests[UINT16_MAX + 1]; /** * Generator for unique request IDs. @@ -350,42 +285,15 @@ static struct RequestSocket sockets[DNS_SOCKET_MAX]; static uint64_t request_id_gen; /** - * IP address to use for the DNS server if we are a DNS exit service - * (for VPN via mesh); otherwise NULL. - */ -static char *dns_exit; - -/** * Handle to the MESH service (for receiving DNS queries), or NULL * if we are not a DNS exit. */ static struct GNUNET_MESH_Handle *mesh; - /** - * We're done with a RequestSocket, close it for now. - * - * @param rs request socket to clean up + * Handle to the DNS Stub resolver. */ -static void -cleanup_rs (struct RequestSocket *rs) -{ - if (NULL != rs->dnsout4) - { - GNUNET_NETWORK_socket_close (rs->dnsout4); - rs->dnsout4 = NULL; - } - if (NULL != rs->dnsout6) - { - GNUNET_NETWORK_socket_close (rs->dnsout6); - rs->dnsout6 = NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) - { - GNUNET_SCHEDULER_cancel (rs->read_task); - rs->read_task = GNUNET_SCHEDULER_NO_TASK; - } -} +static struct GNUNET_DNSSTUB_Context *dnsstub; /** @@ -430,10 +338,10 @@ cleanup_task (void *cls GNUNET_UNUSED, GNUNET_STATISTICS_destroy (stats, GNUNET_NO); stats = NULL; } - if (NULL != dns_exit) + if (NULL != dnsstub) { - GNUNET_free (dns_exit); - dns_exit = NULL; + GNUNET_DNSSTUB_stop (dnsstub); + dnsstub = NULL; } if (NULL != mesh) { @@ -444,54 +352,6 @@ cleanup_task (void *cls GNUNET_UNUSED, /** - * Open source port for sending DNS requests - * - * @param af AF_INET or AF_INET6 - * @return GNUNET_OK on success - */ -static struct GNUNET_NETWORK_Handle * -open_socket (int af) -{ - struct sockaddr_in a4; - struct sockaddr_in6 a6; - struct sockaddr *sa; - socklen_t alen; - struct GNUNET_NETWORK_Handle *ret; - - ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0); - if (NULL == ret) - return NULL; - switch (af) - { - case AF_INET: - memset (&a4, 0, alen = sizeof (struct sockaddr_in)); - sa = (struct sockaddr *) &a4; - break; - case AF_INET6: - memset (&a6, 0, alen = sizeof (struct sockaddr_in6)); - sa = (struct sockaddr *) &a6; - break; - default: - GNUNET_break (0); - GNUNET_NETWORK_socket_close (ret); - return NULL; - } - sa->sa_family = af; - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, - sa, - alen)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Could not bind to any port: %s\n"), - STRERROR (errno)); - GNUNET_NETWORK_socket_close (ret); - return NULL; - } - return ret; -} - - -/** * We're done with some request, finish processing. * * @param rr request send to the network or just clean up. @@ -677,70 +537,21 @@ send_request_to_client (struct RequestRecord *rr, } -/** - * 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); - /** - * Get a socket of the specified address family to send out a - * UDP DNS request to the Internet. + * Callback called from DNSSTUB resolver when a resolution + * succeeded. * - * @param af desired address family - * @return NULL on error (given AF not "supported") + * @param cls NULL + * @param rs the socket that received the response + * @param dns the response itself + * @param r number of bytes in dns */ -static struct GNUNET_NETWORK_Handle * -get_request_socket (int af) -{ - struct RequestSocket *rs; - struct GNUNET_NETWORK_FDSet *rset; - static struct GNUNET_NETWORK_Handle *ret; - - rs = &sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - DNS_SOCKET_MAX)]; - rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); - switch (af) - { - case AF_INET: - if (NULL == rs->dnsout4) - rs->dnsout4 = open_socket (AF_INET); - ret = rs->dnsout4; - break; - case AF_INET6: - if (NULL == rs->dnsout6) - rs->dnsout6 = open_socket (AF_INET6); - ret = rs->dnsout6; - break; - default: - return NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) - { - GNUNET_SCHEDULER_cancel (rs->read_task); - rs->read_task = GNUNET_SCHEDULER_NO_TASK; - } - 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); - if (NULL != 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); - GNUNET_NETWORK_fdset_destroy (rset); - return ret; -} +static void +process_dns_result (void *cls, + struct GNUNET_DNSSTUB_RequestSocket *rs, + const struct GNUNET_TUN_DnsHeader *dns, + size_t r); /** @@ -815,8 +626,14 @@ next_phase (struct RequestRecord *rr) } rr->phase = RP_INTERNET_DNS; - rr->dnsout = get_request_socket (rr->dst_addr.ss_family); - if (NULL == rr->dnsout) + rr->rs = GNUNET_DNSSTUB_resolve (dnsstub, + (struct sockaddr*) &rr->dst_addr, + salen, + rr->payload, + rr->payload_length, + &process_dns_result, + NULL); + if (NULL == rr->rs) { GNUNET_STATISTICS_update (stats, gettext_noop ("# DNS exit failed (failed to open socket)"), @@ -824,12 +641,6 @@ next_phase (struct RequestRecord *rr) cleanup_rr (rr); return; } - GNUNET_NETWORK_socket_sendto (rr->dnsout, - rr->payload, - rr->payload_length, - (struct sockaddr*) &rr->dst_addr, - salen); - rr->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); return; case RP_INTERNET_DNS: rr->phase = RP_MODIFY; @@ -956,68 +767,32 @@ transmit_reply_to_mesh (void *cls, } + /** - * Actually do the reading of a DNS packet from our UDP socket and see - * if we have a valid, matching, pending request. + * Callback called from DNSSTUB resolver when a resolution + * succeeded. * - * @param dnsout socket to read from - * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket) + * @param cls NULL + * @param rs the socket that received the response + * @param dns the response itself + * @param r number of bytes in dns */ -static int -do_dns_read (struct GNUNET_NETWORK_Handle *dnsout) +static void +process_dns_result (void *cls, + struct GNUNET_DNSSTUB_RequestSocket *rs, + const struct GNUNET_TUN_DnsHeader *dns, + size_t r) { - struct sockaddr_storage addr; - socklen_t addrlen; - struct GNUNET_TUN_DnsHeader *dns; struct RequestRecord *rr; struct TunnelState *ts; - ssize_t r; - int len; - -#ifndef MINGW - if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) - { - /* conservative choice: */ - len = UINT16_MAX; - } -#else - /* port the code above? */ - len = UINT16_MAX; -#endif - - { - 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); - if (-1 == r) - { - 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, - _("Received DNS response that is too small (%u bytes)"), - r); - return GNUNET_NO; - } - dns = (struct GNUNET_TUN_DnsHeader *) buf; - /* Handle case that this is a reply to a request from a MESH DNS tunnel */ - ts = tunnels[dns->id]; - if ( (NULL == ts) || - (ts->dnsout != dnsout) || - (addrlen != ts->addrlen) || - (0 != memcmp (&ts->addr, - &addr, - addrlen)) || - (0 == GNUNET_TIME_absolute_get_remaining (ts->timeout).rel_value) ) - ts = NULL; /* DNS responder address missmatch */ - if (NULL != ts) + + GNUNET_assert (NULL == cls); + /* Handle case that this is a reply to a request from a MESH DNS tunnel */ + ts = tunnels[dns->id]; + if ( (NULL == ts) || + (ts->rs != rs) ) + ts = NULL; /* DNS responder address missmatch */ + if (NULL != ts) { tunnels[dns->id] = NULL; GNUNET_free_non_null (ts->reply); @@ -1034,76 +809,25 @@ do_dns_read (struct GNUNET_NETWORK_Handle *dnsout) &transmit_reply_to_mesh, ts); } - /* Handle case that this is a reply to a local request (intercepted from TUN interface) */ - rr = &requests[dns->id]; - if ( (rr->phase != RP_INTERNET_DNS) || - (rr->dnsout != dnsout) || - (0 != memcmp (&rr->dst_addr, - &addr, - addrlen)) || - (0 == GNUNET_TIME_absolute_get_remaining (rr->timeout).rel_value) ) + /* Handle case that this is a reply to a local request (intercepted from TUN interface) */ + rr = &requests[dns->id]; + if ( (rr->phase != RP_INTERNET_DNS) || + (rr->rs != rs) ) { if (NULL == ts) - { - /* unexpected / bogus reply */ - GNUNET_STATISTICS_update (stats, - gettext_noop ("# External DNS response discarded (no matching request)"), - 1, GNUNET_NO); - } - return GNUNET_NO; + { + /* unexpected / bogus reply */ + GNUNET_STATISTICS_update (stats, + gettext_noop ("# External DNS response discarded (no matching request)"), + 1, GNUNET_NO); + } + return; } - GNUNET_free_non_null (rr->payload); - rr->payload = GNUNET_malloc (r); - memcpy (rr->payload, buf, r); - rr->payload_length = r; - next_phase (rr); - } - return GNUNET_OK; -} - - -/** - * 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) -{ - struct RequestSocket *rs = cls; - struct GNUNET_NETWORK_FDSet *rset; - - rs->read_task = GNUNET_SCHEDULER_NO_TASK; - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) - { - /* timeout or shutdown */ - 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->dnsout4))) - rs->dnsout4 = NULL; - if ((NULL != rs->dnsout6) && - (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) && - (GNUNET_SYSERR == do_dns_read (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); - if (NULL != 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); - GNUNET_NETWORK_fdset_destroy (rset); + GNUNET_free_non_null (rr->payload); + rr->payload = GNUNET_malloc (r); + memcpy (rr->payload, dns, r); + rr->payload_length = r; + next_phase (rr); } @@ -1410,11 +1134,7 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, size_t dlen = mlen - sizeof (struct GNUNET_MessageHeader); char buf[dlen] GNUNET_ALIGN; struct GNUNET_TUN_DnsHeader *dout; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - struct sockaddr *so; - socklen_t salen; - + if (dlen < sizeof (struct GNUNET_TUN_DnsHeader)) { GNUNET_break_op (0); @@ -1428,51 +1148,14 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, UINT16_MAX + 1); tunnels[ts->my_id] = ts; memcpy (buf, dns, dlen); - dout = (struct GNUNET_TUN_DnsHeader*) buf; + dout = (struct GNUNET_TUN_DnsHeader *) buf; dout->id = ts->my_id; - memset (&v4, 0, sizeof (v4)); - memset (&v6, 0, sizeof (v6)); - if (1 == inet_pton (AF_INET, dns_exit, &v4.sin_addr)) - { - salen = sizeof (v4); - v4.sin_family = AF_INET; - v4.sin_port = htons (53); -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = (u_char) salen; -#endif - so = (struct sockaddr *) &v4; - ts->dnsout = get_request_socket (AF_INET); - } - else if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr)) - { - salen = sizeof (v6); - v6.sin6_family = AF_INET6; - v6.sin6_port = htons (53); -#if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = (u_char) salen; -#endif - so = (struct sockaddr *) &v6; - ts->dnsout = get_request_socket (AF_INET6); - } - else - { - GNUNET_break (0); + ts->rs = GNUNET_DNSSTUB_resolve2 (dnsstub, + buf, dlen, + &process_dns_result, + NULL); + if (NULL == ts->rs) return GNUNET_SYSERR; - } - if (NULL == ts->dnsout) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Configured DNS exit `%s' is not working / valid.\n"), - dns_exit); - return GNUNET_SYSERR; - } - memcpy (&ts->addr, - so, - salen); - ts->addrlen = salen; - GNUNET_NETWORK_socket_sendto (ts->dnsout, - buf, dlen, so, salen); - ts->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); return GNUNET_OK; } @@ -1552,6 +1235,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, char *ipv6prefix; struct in_addr dns_exit4; struct in6_addr dns_exit6; + char *dns_exit; cfg = cfg_; if (GNUNET_YES != @@ -1582,7 +1266,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, GNUNET_free_non_null (dns_exit); dns_exit = NULL; } - + dnsstub = GNUNET_DNSSTUB_start (dns_exit); helper_argv[0] = GNUNET_strdup ("gnunet-dns"); if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IFNAME", &ifc_name)) |