From ff4d7b51f37f61633766664647e9b148af1e4f0a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 19 Apr 2018 18:38:41 +0200 Subject: [PATCH] support multiple DNS resolvers for queries (in DNSSTUB and GNS2DNS resolution for now) --- src/dns/dnsstub.c | 683 +++++++++------- src/dns/gnunet-service-dns.c | 43 +- src/dns/gnunet-zoneimport.c | 24 +- src/exit/gnunet-daemon-exit.c | 29 +- src/gns/gnunet-dns2gns.c | 19 +- src/gns/gnunet-service-gns_resolver.c | 1072 ++++++++++++++----------- src/gns/test_gns_lookup.conf | 2 +- src/include/gnunet_dnsstub_lib.h | 85 +- src/namestore/gnunet-zoneimport.c | 35 +- 9 files changed, 1107 insertions(+), 885 deletions(-) diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c index 6aa2d7b8f..6eb3612c2 100644 --- a/src/dns/dnsstub.c +++ b/src/dns/dnsstub.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. + Copyright (C) 2012, 2018 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 @@ -27,21 +27,16 @@ #include "gnunet_tun_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) - /** * 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) + * DNS Server used for resolution. */ -#define DNS_SOCKET_MAX 128 +struct DnsServer; /** @@ -81,19 +76,14 @@ struct GNUNET_DNSSTUB_RequestSocket struct GNUNET_SCHEDULER_Task *retry_task; /** - * When should this request time out? - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Address we sent the DNS request to. + * Next address we sent the DNS request to. */ - struct sockaddr_storage addr; + struct DnsServer *ds_pos; /** - * Number of bytes in @e addr. + * Context this request executes in. */ - socklen_t addrlen; + struct GNUNET_DNSSTUB_Context *ctx; /** * Query we sent to @e addr. @@ -108,6 +98,29 @@ struct GNUNET_DNSSTUB_RequestSocket }; +/** + * DNS Server used for resolution. + */ +struct DnsServer +{ + + /** + * Kept in a DLL. + */ + struct DnsServer *next; + + /** + * Kept in a DLL. + */ + struct DnsServer *prev; + + /** + * IP address of the DNS resolver. + */ + struct sockaddr_storage ss; +}; + + /** * Handle to the stub resolver. */ @@ -117,13 +130,28 @@ struct GNUNET_DNSSTUB_Context /** * Array of all open sockets for DNS requests. */ - struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX]; + struct GNUNET_DNSSTUB_RequestSocket *sockets; + + /** + * DLL of DNS resolvers we use. + */ + struct DnsServer *dns_head; + + /** + * DLL of DNS resolvers we use. + */ + struct DnsServer *dns_tail; + + /** + * How frequently do we retry requests? + */ + struct GNUNET_TIME_Relative retry_freq; /** - * IP address to use for the DNS server if we are a DNS exit service - * (for VPN via cadet); otherwise NULL. + * Length of @e sockets array. */ - char *dns_exit; + unsigned int num_sockets; + }; @@ -211,34 +239,22 @@ open_socket (int af) } -/** - * Read a DNS response from the (unhindered) UDP-Socket - * - * @param cls socket to read from - */ -static void -read_response (void *cls); - - /** * Get a socket of the specified address family to send out a * UDP DNS request to the Internet. * * @param ctx the DNSSTUB context - * @param af desired address family - * @return NULL on error (given AF not "supported") + * @return NULL on error */ static struct GNUNET_DNSSTUB_RequestSocket * -get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, - int af) +get_request_socket (struct GNUNET_DNSSTUB_Context *ctx) { struct GNUNET_DNSSTUB_RequestSocket *rs; - struct GNUNET_NETWORK_FDSet *rset; for (unsigned int i=0;i<256;i++) { rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - DNS_SOCKET_MAX)]; + ctx->num_sockets)]; if (NULL == rs->rc) break; } @@ -246,25 +262,10 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, { /* signal "failure" */ rs->rc (rs->rc_cls, - rs, NULL, 0); rs->rc = NULL; } - 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 (NULL != rs->read_task) { GNUNET_SCHEDULER_cancel (rs->read_task); @@ -280,194 +281,7 @@ get_request_socket (struct GNUNET_DNSSTUB_Context *ctx, 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); - 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; -} - - -/** - * 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 length of @a sa - * @param request DNS request to transmit - * @param request_len number of bytes in @a request - * @param rc function to call with result - * @param rc_cls closure for @a 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; - - if (NULL == (rs = get_request_socket (ctx, - sa->sa_family))) - return NULL; - GNUNET_assert (NULL == rs->rc); - GNUNET_memcpy (&rs->addr, - sa, - sa_len); - rs->addrlen = sa_len; - rs->rc = rc; - rs->rc_cls = rc_cls; - rs->request = GNUNET_memdup (request, - request_len); - rs->request_len = request_len; - rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, - rs); - 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 *sa; - 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 - sa = (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 - sa = (struct sockaddr *) &v6; - af = AF_INET6; - } - else - { - GNUNET_break (0); - return NULL; - } - if (NULL == (rs = get_request_socket (ctx, - af))) - return NULL; - GNUNET_assert (NULL == rs->rc); - 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; - } - 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)) - 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); + rs->ctx = ctx; return rs; } @@ -484,9 +298,7 @@ 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; + struct GNUNET_DNSSTUB_Context *ctx = rs->ctx; ssize_t r; int len; @@ -507,9 +319,15 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, len); { unsigned char buf[len] GNUNET_ALIGN; + int found; + struct sockaddr_storage addr; + socklen_t addrlen; + struct GNUNET_TUN_DnsHeader *dns; addrlen = sizeof (addr); - memset (&addr, 0, sizeof (addr)); + memset (&addr, + 0, + sizeof (addr)); r = GNUNET_NETWORK_socket_recvfrom (dnsout, buf, sizeof (buf), @@ -522,6 +340,24 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, GNUNET_NETWORK_socket_close (dnsout); return GNUNET_SYSERR; } + found = GNUNET_NO; + for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next) + { + if (0 == memcmp (&addr, + &ds->ss, + GNUNET_MIN (sizeof (struct sockaddr_storage), + addrlen))) + { + found = GNUNET_YES; + break; + } + } + if (GNUNET_NO == found) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DNS response from server we never asked (ignored)"); + return GNUNET_NO; + } if (sizeof (struct GNUNET_TUN_DnsHeader) > r) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -530,22 +366,15 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, return GNUNET_NO; } dns = (struct GNUNET_TUN_DnsHeader *) buf; - if ( (addrlen != rs->addrlen) || - (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) ) + if (NULL == rs->rc) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Request timeout or invalid sender address; ignoring reply\n"); + "Request timeout or cancelled; ignoring reply\n"); return GNUNET_NO; } - if (NULL != rs->rc) - rs->rc (rs->rc_cls, - rs, - dns, - r); + rs->rc (rs->rc_cls, + dns, + r); } return GNUNET_OK; } @@ -557,44 +386,21 @@ do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs, * @param cls socket to read from */ static void -read_response (void *cls) +read_response (void *cls); + + +/** + * Schedule #read_response() task for @a rs. + * + * @param rs request to schedule read operation for + */ +static void +schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs) { - struct GNUNET_DNSSTUB_RequestSocket *rs = cls; struct GNUNET_NETWORK_FDSet *rset; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - rs->read_task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) - { - /* 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))) - 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 */ + if (NULL != rs->read_task) + GNUNET_SCHEDULER_cancel (rs->read_task); rset = GNUNET_NETWORK_fdset_create (); if (NULL != rs->dnsout4) GNUNET_NETWORK_fdset_set (rset, @@ -603,7 +409,7 @@ read_response (void *cls) 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), + GNUNET_TIME_UNIT_FOREVER_REL, rset, NULL, &read_response, @@ -612,6 +418,151 @@ read_response (void *cls) } +/** + * Read a DNS response from the (unhindered) UDP-Socket + * + * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from + */ +static void +read_response (void *cls) +{ + struct GNUNET_DNSSTUB_RequestSocket *rs = cls; + const struct GNUNET_SCHEDULER_TaskContext *tc; + + rs->read_task = NULL; + tc = GNUNET_SCHEDULER_get_task_context (); + /* 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 */ + schedule_read (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_DNSSTUB_Context *ctx = rs->ctx; + const struct sockaddr *sa; + socklen_t salen; + struct DnsServer *ds; + struct GNUNET_NETWORK_Handle *dnsout; + + rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq, + &transmit_query, + rs); + ds = rs->ds_pos; + rs->ds_pos = ds->next; + if (NULL == rs->ds_pos) + rs->ds_pos = ctx->dns_head; + GNUNET_assert (NULL != ds); + dnsout = NULL; + switch (ds->ss.ss_family) + { + case AF_INET: + if (NULL == rs->dnsout4) + rs->dnsout4 = open_socket (AF_INET); + dnsout = rs->dnsout4; + sa = (const struct sockaddr *) &ds->ss; + salen = sizeof (struct sockaddr_in); + break; + case AF_INET6: + if (NULL == rs->dnsout6) + rs->dnsout6 = open_socket (AF_INET6); + dnsout = rs->dnsout6; + sa = (const struct sockaddr *) &ds->ss; + salen = sizeof (struct sockaddr_in6); + break; + default: + return; + } + if (NULL == dnsout) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to use configure DNS server, skipping\n"); + return; + } + if (GNUNET_SYSERR == + GNUNET_NETWORK_socket_sendto (dnsout, + rs->request, + rs->request_len, + sa, + salen)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to send DNS request to %s\n"), + GNUNET_a2s (sa, + salen)); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Sent DNS request to %s\n"), + GNUNET_a2s (sa, + salen)); + schedule_read (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_resolve (struct GNUNET_DNSSTUB_Context *ctx, + const void *request, + size_t request_len, + GNUNET_DNSSTUB_ResultCallback rc, + void *rc_cls) +{ + struct GNUNET_DNSSTUB_RequestSocket *rs; + + if (NULL == ctx->dns_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No DNS server configured for resolution\n"); + return NULL; + } + if (NULL == (rs = get_request_socket (ctx))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No request socket available for DNS resolution\n"); + return NULL; + } + rs->ds_pos = ctx->dns_head; + rs->rc = rc; + rs->rc_cls = rc_cls; + rs->request = GNUNET_memdup (request, + request_len); + rs->request_len = request_len; + rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, + rs); + return rs; +} + + /** * Cancel DNS resolution. * @@ -626,27 +577,152 @@ GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs) GNUNET_SCHEDULER_cancel (rs->retry_task); rs->retry_task = NULL; } + if (NULL != rs->read_task) + { + GNUNET_SCHEDULER_cancel (rs->read_task); + rs->read_task = NULL; + } } /** * Start a DNS stub resolver. * - * @param dns_ip target IP address to use + * @param num_sockets how many sockets should we open + * in parallel for DNS queries for this stub? * @return NULL on error */ struct GNUNET_DNSSTUB_Context * -GNUNET_DNSSTUB_start (const char *dns_ip) +GNUNET_DNSSTUB_start (unsigned int num_sockets) { struct GNUNET_DNSSTUB_Context *ctx; + if (0 == num_sockets) + { + GNUNET_break (0); + return NULL; + } ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context); - if (NULL != dns_ip) - ctx->dns_exit = GNUNET_strdup (dns_ip); + ctx->num_sockets = num_sockets; + ctx->sockets = GNUNET_new_array (num_sockets, + struct GNUNET_DNSSTUB_RequestSocket); + ctx->retry_freq = DNS_RETRANSMIT_DELAY; return ctx; } +/** + * Add nameserver for use by the DNSSTUB. We will use + * all provided nameservers for resolution (round-robin). + * + * @param ctx resolver context to modify + * @param dns_ip target IP address to use (as string) + * @return #GNUNET_OK on success + */ +int +GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx, + const char *dns_ip) +{ + struct DnsServer *ds; + struct in_addr i4; + struct in6_addr i6; + + ds = GNUNET_new (struct DnsServer); + if (1 == inet_pton (AF_INET, + dns_ip, + &i4)) + { + struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss; + + s4->sin_family = AF_INET; + s4->sin_port = htons (53); + s4->sin_addr = i4; +#if HAVE_SOCKADDR_IN_SIN_LEN + s4->sin_len = (u_char) sizeof (struct sockaddr_in); +#endif + } + else if (1 == inet_pton (AF_INET6, + dns_ip, + &i6)) + { + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss; + + s6->sin6_family = AF_INET6; + s6->sin6_port = htons (53); + s6->sin6_addr = i6; +#if HAVE_SOCKADDR_IN_SIN_LEN + s6->sin6_len = (u_char) sizeof (struct sockaddr_in6); +#endif + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Malformed IP address `%s' for DNS server\n", + dns_ip); + GNUNET_free (ds); + return GNUNET_SYSERR; + } + GNUNET_CONTAINER_DLL_insert (ctx->dns_head, + ctx->dns_tail, + ds); + return GNUNET_OK; +} + + +/** + * Add nameserver for use by the DNSSTUB. We will use + * all provided nameservers for resolution (round-robin). + * + * @param ctx resolver context to modify + * @param sa socket address of DNS resolver to use + * @return #GNUNET_OK on success + */ +int +GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx, + const struct sockaddr *sa) +{ + struct DnsServer *ds; + + ds = GNUNET_new (struct DnsServer); + switch (sa->sa_family) + { + case AF_INET: + memcpy (&ds->ss, + sa, + sizeof (struct sockaddr_in)); + break; + case AF_INET6: + memcpy (&ds->ss, + sa, + sizeof (struct sockaddr_in6)); + break; + default: + GNUNET_break (0); + GNUNET_free (ds); + return GNUNET_SYSERR; + } + GNUNET_CONTAINER_DLL_insert (ctx->dns_head, + ctx->dns_tail, + ds); + return GNUNET_OK; +} + + +/** + * How long should we try requests before timing out? + * Only effective for requests issued after this call. + * + * @param ctx resolver context to modify + * @param retry_freq how long to wait between retries + */ +void +GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx, + struct GNUNET_TIME_Relative retry_freq) +{ + ctx->retry_freq = retry_freq; +} + + /** * Cleanup DNSSTUB resolver. * @@ -655,15 +731,18 @@ GNUNET_DNSSTUB_start (const char *dns_ip) void GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx) { - unsigned int i; + struct DnsServer *ds; - for (i=0;isockets[i]); - if (NULL != ctx->dns_exit) + while (NULL != (ds = ctx->dns_head)) { - GNUNET_free (ctx->dns_exit); - ctx->dns_exit = NULL; + GNUNET_CONTAINER_DLL_remove (ctx->dns_head, + ctx->dns_tail, + ds); + GNUNET_free (ds); } + for (unsigned int i=0;inum_sockets;i++) + cleanup_rs (&ctx->sockets[i]); + GNUNET_free (ctx->sockets); GNUNET_free (ctx); } diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c index 9feaa8413..39ce7f6e5 100644 --- a/src/dns/gnunet-service-dns.c +++ b/src/dns/gnunet-service-dns.c @@ -508,13 +508,11 @@ send_request_to_client (struct RequestRecord *rr, * succeeded. * * @param cls NULL - * @param rs the socket that received the response * @param dns the response itself * @param r number of bytes in dns */ static void process_dns_result (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t r); @@ -530,7 +528,6 @@ next_phase (struct RequestRecord *rr) { struct ClientRecord *cr; int nz; - socklen_t salen; if (rr->phase == RP_DROP) { @@ -582,22 +579,27 @@ next_phase (struct RequestRecord *rr) next_phase (rr); return; case RP_QUERY: +#if 0 + /* TODO: optionally, use this to forward DNS requests to the + *original* DNS server instead of the one we have configured... + (but then we need to create a fresh dnsstub for each request + *and* manage the timeout) */ switch (rr->dst_addr.ss_family) { case AF_INET: salen = sizeof (struct sockaddr_in); + sa = (const struct sockaddr *) &rr->dst_addr; break; case AF_INET6: salen = sizeof (struct sockaddr_in6); + sa = (const struct sockaddr *) &rr->dst_addr; break; default: GNUNET_assert (0); } - +#endif rr->phase = RP_INTERNET_DNS; rr->rs = GNUNET_DNSSTUB_resolve (dnsstub, - (struct sockaddr*) &rr->dst_addr, - salen, rr->payload, rr->payload_length, &process_dns_result, @@ -714,13 +716,11 @@ client_disconnect_cb (void *cls, * succeeded. * * @param cls NULL - * @param rs the socket that received the response * @param dns the response itself * @param r number of bytes in dns */ static void process_dns_result (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t r) { @@ -733,8 +733,7 @@ process_dns_result (void *cls, return; /* ignore */ rr = &requests[dns->id]; - if ( (rr->phase != RP_INTERNET_DNS) || - (rr->rs != rs) ) + if (rr->phase != RP_INTERNET_DNS) { /* unexpected / bogus reply */ GNUNET_STATISTICS_update (stats, @@ -1055,8 +1054,6 @@ run (void *cls, char *ipv4mask; char *ipv6addr; char *ipv6prefix; - struct in_addr dns_exit4; - struct in6_addr dns_exit6; char *dns_exit; char *binary; int nortsetup; @@ -1065,24 +1062,26 @@ run (void *cls, stats = GNUNET_STATISTICS_create ("dns", cfg); GNUNET_SCHEDULER_add_shutdown (&cleanup_task, cls); + dnsstub = GNUNET_DNSSTUB_start (128); + /* TODO: support multiple DNS_EXIT servers being configured */ + /* TODO: see above TODO on using DNS server from original packet. + Not sure which is best... */ dns_exit = NULL; - if ( ( (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "dns", - "DNS_EXIT", - &dns_exit)) || - ( (1 != inet_pton (AF_INET, dns_exit, &dns_exit4)) && - (1 != inet_pton (AF_INET6, dns_exit, &dns_exit6)) ) ) ) + if ( (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "dns", + "DNS_EXIT", + &dns_exit)) || + (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (dnsstub, + dns_exit)) ) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "dns", "DNS_EXIT", _("need a valid IPv4 or IPv6 address\n")); GNUNET_free_non_null (dns_exit); - dns_exit = NULL; } - dnsstub = GNUNET_DNSSTUB_start (dns_exit); - GNUNET_free_non_null (dns_exit); binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns"); if (GNUNET_YES != GNUNET_OS_check_helper_binary (binary, diff --git a/src/dns/gnunet-zoneimport.c b/src/dns/gnunet-zoneimport.c index 914868af4..860672e7a 100644 --- a/src/dns/gnunet-zoneimport.c +++ b/src/dns/gnunet-zoneimport.c @@ -290,13 +290,11 @@ process_record (struct Request *req, * Function called with the result of a DNS resolution. * * @param cls closure with the `struct Request` - * @param rs socket that received the response * @param dns dns response, never NULL * @param dns_len number of bytes in @a dns */ static void process_result (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t dns_len) { @@ -407,11 +405,11 @@ submit_req (struct Request *req) (pending >= THRESH) ) return GNUNET_SYSERR; GNUNET_assert (NULL == req->rs); - req->rs = GNUNET_DNSSTUB_resolve2 (ctx, - req->raw, - req->raw_len, - &process_result, - req); + req->rs = GNUNET_DNSSTUB_resolve (ctx, + req->raw, + req->raw_len, + &process_result, + req); GNUNET_assert (NULL != req->rs); req->issue_num++; last_request = now; @@ -561,13 +559,23 @@ main (int argc, "Missing required configuration argument\n"); return -1; } - ctx = GNUNET_DNSSTUB_start (argv[1]); + ctx = GNUNET_DNSSTUB_start (256); if (NULL == ctx) { fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n"); return 1; } + if (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (ctx, + argv[1])) + { + fprintf (stderr, + "Failed to use `%s' for DNS resolver\n", + argv[1]); + return 1; + } + while (NULL != fgets (hn, sizeof (hn), diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c index 0b3cc505a..5cb1ebfd9 100644 --- a/src/exit/gnunet-daemon-exit.c +++ b/src/exit/gnunet-daemon-exit.c @@ -458,13 +458,11 @@ GNUNET_NETWORK_STRUCT_END * succeeded. * * @param cls NULL - * @param rs the socket that received the response * @param dns the response itself * @param r number of bytes in @a dns */ static void process_dns_result (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t r) { @@ -479,8 +477,7 @@ process_dns_result (void *cls, return; /* Handle case that this is a reply to a request from a CADET DNS channel */ ts = channels[dns->id]; - if ( (NULL == ts) || - (ts->specifics.dns.rs != rs) ) + if (NULL == ts) return; LOG (GNUNET_ERROR_TYPE_DEBUG, "Got a response from the stub resolver for DNS request received via CADET!\n"); @@ -557,11 +554,11 @@ handle_dns_request (void *cls, dlen); dout = (struct GNUNET_TUN_DnsHeader *) buf; dout->id = ts->specifics.dns.my_id; - ts->specifics.dns.rs = GNUNET_DNSSTUB_resolve2 (dnsstub, - buf, - dlen, - &process_dns_result, - NULL); + ts->specifics.dns.rs = GNUNET_DNSSTUB_resolve (dnsstub, + buf, + dlen, + &process_dns_result, + NULL); if (NULL == ts->specifics.dns.rs) { GNUNET_break_op (0); @@ -3545,25 +3542,23 @@ advertise_dns_exit () }; char *dns_exit; struct GNUNET_HashCode port; - struct in_addr dns_exit4; - struct in6_addr dns_exit6; if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "EXIT_DNS")) return; + GNUNET_assert (NULL != (dnsstub = GNUNET_DNSSTUB_start (128))); + dns_exit = NULL; + /* TODO: support using multiple DNS resolvers */ if ( (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "DNS_RESOLVER", &dns_exit)) || - ( (1 != inet_pton (AF_INET, - dns_exit, - &dns_exit4)) && - (1 != inet_pton (AF_INET6, - dns_exit, - &dns_exit6)) ) ) + (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (dnsstub, + dns_exit)) ) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "dns", diff --git a/src/gns/gnunet-dns2gns.c b/src/gns/gnunet-dns2gns.c index 47cc6dde0..bc66f1325 100644 --- a/src/gns/gnunet-dns2gns.c +++ b/src/gns/gnunet-dns2gns.c @@ -247,19 +247,16 @@ do_timeout (void *cls) * Iterator called on obtained result for a DNS lookup * * @param cls closure - * @param rs the request socket * @param dns the DNS udp payload * @param r size of the DNS payload */ static void dns_result_processor (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t r) { struct Request *request = cls; - (void) rs; if (NULL == dns) { /* DNSSTUB gave up, so we trigger timeout early */ @@ -307,11 +304,11 @@ result_processor (void *cls, request->original_request_id = request->packet->id; GNUNET_DNSPARSER_free_packet (request->packet); request->packet = NULL; - request->dns_lookup = GNUNET_DNSSTUB_resolve2 (dns_stub, - request->udp_msg, - request->udp_msg_size, - &dns_result_processor, - request); + request->dns_lookup = GNUNET_DNSSTUB_resolve (dns_stub, + request->udp_msg, + request->udp_msg_size, + &dns_result_processor, + request); return; } packet = request->packet; @@ -594,8 +591,12 @@ run (void *cls, NULL); if (NULL == (gns = GNUNET_GNS_connect (cfg))) return; - if (NULL == (dns_stub = GNUNET_DNSSTUB_start (dns_ip))) + GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128))); + if (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (dns_stub, + dns_ip)) { + GNUNET_DNSSTUB_stop (dns_stub); GNUNET_GNS_disconnect (gns); gns = NULL; return; diff --git a/src/gns/gnunet-service-gns_resolver.c b/src/gns/gnunet-service-gns_resolver.c index 94819b040..74eb47f29 100644 --- a/src/gns/gnunet-service-gns_resolver.c +++ b/src/gns/gnunet-service-gns_resolver.c @@ -68,6 +68,57 @@ #define MAX_RECURSION 256 +/** + * DLL to hold the authority chain we had to pass in the resolution + * process. + */ +struct AuthorityChain; + + +/** + * Element of a resolution process for looking up the + * responsible DNS server hostname in a GNS2DNS recursive + * resolution. + */ +struct Gns2DnsPending +{ + + /** + * Kept in a DLL. + */ + struct Gns2DnsPending *next; + + /** + * Kept in a DLL. + */ + struct Gns2DnsPending *prev; + + /** + * Context this activity belongs with. + */ + struct AuthorityChain *ac; + + /** + * Handle for the resolution of the IP part of the + * GNS2DNS record. Will return to us the addresses + * of the DNS resolver to use. + */ + struct GNS_ResolverHandle *rh; + + /** + * Handle for DNS resolution of the DNS nameserver. + */ + struct GNUNET_RESOLVER_RequestHandle *dns_rh; +}; + + +/** + * Handle to a currenty pending resolution. On result (positive or + * negative) the #GNS_ResultProcessor is called. + */ +struct GNS_ResolverHandle; + + /** * DLL to hold the authority chain we had to pass in the resolution * process. @@ -131,11 +182,32 @@ struct AuthorityChain char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1]; /** - * IP address of the DNS resolver that is authoritative. - * (this implementation currently only supports one - * IP at a time). + * List of resolutions of the 'ip' of the name server that + * are still pending. + */ + struct Gns2DnsPending *gp_head; + + /** + * Tail of list of resolutions of the 'ip' of the name server that + * are still pending. + */ + struct Gns2DnsPending *gp_tail; + + /** + * Handle to perform DNS lookups with this authority (in GNS2DNS handling). + */ + struct GNUNET_DNSSTUB_Context *dns_handle; + + /** + * Did we succeed in getting an IP address for *any* of the DNS servers listed? + * Once we do, we can start with DNS queries. + */ + int found; + + /** + * Did we start the recursive resolution via DNS? */ - struct sockaddr_storage dns_ip; + int launched; } dns_authority; @@ -217,34 +289,6 @@ struct VpnContext }; -/** - * Information we keep during the resolution of an - * IP address for a DNS server while handling a - * GNS2DNS record. - */ -struct Gns2DnsContext -{ - - /** - * DNS domain in which the resolution will continue - * (first part of the GNS2DNS record). - */ - char *ns; - - /** - * Handle for the resolution of the IP part of the - * GNS2DNS record. Will return to us the addresses - * of the DNS resolver to use. - */ - struct GNS_ResolverHandle *rh; - - /** - * Handle for DNS resolution of the DNS nameserver. - */ - struct GNUNET_RESOLVER_RequestHandle *dns_rh; -}; - - /** * Handle to a currenty pending resolution. On result (positive or * negative) the #GNS_ResultProcessor is called. @@ -277,12 +321,6 @@ struct GNS_ResolverHandle */ void* proc_cls; - /** - * Handle used during GNS2DNS resolution for looking up the - * IP address of the DNS server. - */ - struct Gns2DnsContext *g2dc; - /** * Handle for DHT lookups. should be NULL if no lookups are in progress */ @@ -430,11 +468,6 @@ static struct GNUNET_VPN_Handle *vpn_handle; */ static struct GNUNET_DHT_Handle *dht_handle; -/** - * Handle to perform DNS lookups. - */ -static struct GNUNET_DNSSTUB_Context *dns_handle; - /** * Heap for limiting parallel DHT lookups */ @@ -543,21 +576,53 @@ translate_dot_plus (struct GNS_ResolverHandle *rh, /** - * Task scheduled to asynchronously fail a resolution. + * Wrapper around #GNS_resolver_lookup_cancel() as a task. + * Used for delayed cleanup so we can unwind the stack first. * - * @param cls the 'struct GNS_ResolverHandle' of the resolution to fail + * @param cls the `struct GNS_ResolverHandle` */ static void -fail_resolution (void *cls) +GNS_resolver_lookup_cancel_ (void *cls) { struct GNS_ResolverHandle *rh = cls; rh->task_id = NULL; - rh->proc (rh->proc_cls, 0, NULL); GNS_resolver_lookup_cancel (rh); } +/** + * Function called to asynchronously fail a resolution. + * + * @param rh the resolution to fail + */ +static void +fail_resolution (struct GNS_ResolverHandle *rh) +{ + rh->proc (rh->proc_cls, + 0, + NULL); + GNUNET_assert (NULL == rh->task_id); + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); +} + + +/** + * Function called when a resolution times out. + * + * @param cls the `struct GNS_ResolverHandle` + */ +static void +timeout_resolution (void *cls) +{ + struct GNS_ResolverHandle *rh = cls; + + rh->task_id = NULL; + fail_resolution (rh); +} + + #if (defined WINDOWS) || (defined DARWIN) /* Don't have this on W32, here's a naive implementation * Was somehow removed on OS X ... */ @@ -831,13 +896,11 @@ start_resolver_lookup (void *cls); * * @param cls the request handle of the resolution that * we were attempting to make - * @param rs socket that received the response * @param dns dns response, never NULL * @param dns_len number of bytes in @a dns */ static void dns_result_parser (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t dns_len) { @@ -845,18 +908,13 @@ dns_result_parser (void *cls, struct GNUNET_DNSPARSER_Packet *p; const struct GNUNET_DNSPARSER_Record *rec; unsigned int rd_count; - unsigned int i; - (void) rs; if (NULL == dns) { rh->dns_request = NULL; GNUNET_SCHEDULER_cancel (rh->task_id); rh->task_id = NULL; - rh->proc (rh->proc_cls, - 0, - NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } if (rh->original_dns_id != dns->id) @@ -872,6 +930,8 @@ dns_result_parser (void *cls, _("Failed to parse DNS response\n")); return; } + + /* We got a result from DNS */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received DNS response for `%s' with %u answers\n", rh->ac_tail->label, @@ -920,8 +980,10 @@ dns_result_parser (void *cls, buf_off = 0; skip = 0; - memset (rd, 0, sizeof (rd)); - for (i=0;inum_answers) rec = &p->answers[i]; @@ -1043,9 +1105,12 @@ dns_result_parser (void *cls, rh->proc (rh->proc_cls, rd_count - skip, rd); - GNS_resolver_lookup_cancel (rh); } GNUNET_DNSPARSER_free_packet (p); + if (NULL != rh->task_id) + GNUNET_SCHEDULER_cancel (rh->task_id); /* should be timeout task */ + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); } @@ -1061,7 +1126,6 @@ static void recursive_dns_resolution (struct GNS_ResolverHandle *rh) { struct AuthorityChain *ac; - socklen_t sa_len; struct GNUNET_DNSPARSER_Query *query; struct GNUNET_DNSPARSER_Packet *p; char *dns_request; @@ -1074,20 +1138,6 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) "Starting DNS lookup for `%s'\n", ac->label); GNUNET_assert (GNUNET_NO == ac->gns_authority); - switch (((const struct sockaddr *) &ac->authority_info.dns_authority.dns_ip)->sa_family) - { - case AF_INET: - sa_len = sizeof (struct sockaddr_in); - break; - case AF_INET6: - sa_len = sizeof (struct sockaddr_in6); - break; - default: - GNUNET_break (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } query = GNUNET_new (struct GNUNET_DNSPARSER_Query); query->name = GNUNET_strdup (ac->label); query->type = rh->record_type; @@ -1109,20 +1159,22 @@ recursive_dns_resolution (struct GNS_ResolverHandle *rh) rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + GNUNET_assert (NULL == rh->task_id); + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); } else { rh->original_dns_id = p->id; - rh->dns_request = GNUNET_DNSSTUB_resolve (dns_handle, - (const struct sockaddr *) &ac->authority_info.dns_authority.dns_ip, - sa_len, + GNUNET_assert (NULL != ac->authority_info.dns_authority.dns_handle); + GNUNET_assert (NULL == rh->dns_request); + rh->dns_request = GNUNET_DNSSTUB_resolve (ac->authority_info.dns_authority.dns_handle, dns_request, dns_request_length, &dns_result_parser, rh); rh->task_id = GNUNET_SCHEDULER_add_delayed (DNS_LOOKUP_TIMEOUT, - &fail_resolution, + &timeout_resolution, rh); } if (GNUNET_SYSERR != ret) @@ -1292,16 +1344,48 @@ vpn_allocation_cb (void *cls, } +/** + * We have resolved one or more of the nameservers for a + * GNS2DNS lookup. Once we have some of them, begin using + * the DNSSTUB resolver. + * + * @param ac context for GNS2DNS resolution + */ +static void +continue_with_gns2dns (struct AuthorityChain *ac) +{ + struct GNS_ResolverHandle *rh = ac->rh; + + if ( (NULL != ac->authority_info.dns_authority.gp_head) && + (GNUNET_NO == ac->authority_info.dns_authority.found) ) + return; /* more pending and none found yet */ + if (GNUNET_NO == ac->authority_info.dns_authority.found) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to resolve DNS server for `%s' in GNS2DNS resolution\n", + ac->authority_info.dns_authority.name); + fail_resolution (rh); + return; + } + if (GNUNET_NO != ac->authority_info.dns_authority.launched) + return; /* already running, do not launch again! */ + /* recurse */ + ac->authority_info.dns_authority.launched = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Will continue resolution using DNS to resolve `%s'\n", + ac->label); + GNUNET_assert (NULL == rh->task_id); + rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, + rh); + +} + + /** * We've resolved the IP address for the DNS resolver to use * after encountering a GNS2DNS record. * - * TODO: Right now we only foward the request to ONE DNS resolver, - * even if we get multiple IP addresses back; a correct implementation - * should try all DNS resolvers. - * - * @param cls the `struct GNS_ResolverHandle` where we encountered - * the GNS2DNS record + * @param cls the `struct Gns2DnsPending` used for this request * @param rd_count number of records in @a rd * @param rd addresses for the DNS resolver (presumably) */ @@ -1310,136 +1394,96 @@ handle_gns2dns_result (void *cls, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd) { - struct GNS_ResolverHandle *rh = cls; - struct AuthorityChain *ac; - struct sockaddr *sa; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - size_t sa_len; - - /* find suitable A/AAAA record */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %u results for IP address of DNS server for GNS2DNS transition\n", - rd_count); - /* enable cleanup of 'rh' handle that comes next... */ - if (NULL != rh->g2dc->rh) + struct Gns2DnsPending *gp = cls; + struct AuthorityChain *ac = gp->ac; + + GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, + ac->authority_info.dns_authority.gp_tail, + gp); + /* enable cleanup of 'rh' handle that automatically comes after we return, + and which expects 'rh' to be in the #rlh_head DLL. */ + if (NULL != gp->rh) { GNUNET_CONTAINER_DLL_insert (rlh_head, rlh_tail, - rh->g2dc->rh); - rh->g2dc->rh = NULL; + gp->rh); + gp->rh = NULL; } - sa = NULL; - sa_len = 0; + GNUNET_free (gp); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u results for IP address of DNS server for GNS2DNS transition\n", + rd_count); + /* find suitable A/AAAA record */ for (unsigned int j=0;jproc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* FIXME: might want to check if we support IPv4 here, - and otherwise skip this one and hope we find another */ - memset (&v4, 0, sizeof (v4)); - sa_len = sizeof (v4); - v4.sin_family = AF_INET; - v4.sin_port = htons (53); + struct sockaddr_in v4; + + if (sizeof (struct in_addr) != rd[j].data_size) + { + GNUNET_break_op (0); + continue; + } + memset (&v4, + 0, + sizeof (v4)); + v4.sin_family = AF_INET; + v4.sin_port = htons (53); #if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = (u_char) sa_len; + v4.sin_len = (u_char) sizeof (v4); #endif - GNUNET_memcpy (&v4.sin_addr, - rd[j].data, - sizeof (struct in_addr)); - sa = (struct sockaddr *) &v4; - break; + GNUNET_memcpy (&v4.sin_addr, + rd[j].data, + sizeof (struct in_addr)); + if (GNUNET_OK == + GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle, + (const struct sockaddr *) &v4)) + ac->authority_info.dns_authority.found = GNUNET_YES; + break; + } case GNUNET_DNSPARSER_TYPE_AAAA: - if (sizeof (struct in6_addr) != rd[j].data_size) { - GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* FIXME: might want to check if we support IPv6 here, - and otherwise skip this one and hope we find another */ - memset (&v6, 0, sizeof (v6)); - sa_len = sizeof (v6); - v6.sin6_family = AF_INET6; - v6.sin6_port = htons (53); + struct sockaddr_in6 v6; + + if (sizeof (struct in6_addr) != rd[j].data_size) + { + GNUNET_break_op (0); + continue; + } + /* FIXME: might want to check if we support IPv6 here, + and otherwise skip this one and hope we find another */ + memset (&v6, + 0, + sizeof (v6)); + v6.sin6_family = AF_INET6; + v6.sin6_port = htons (53); #if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = (u_char) sa_len; + v6.sin6_len = (u_char) sizeof (v6); #endif - GNUNET_memcpy (&v6.sin6_addr, - rd[j].data, - sizeof (struct in6_addr)); - sa = (struct sockaddr *) &v6; - break; + GNUNET_memcpy (&v6.sin6_addr, + rd[j].data, + sizeof (struct in6_addr)); + if (GNUNET_OK == + GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle, + (const struct sockaddr *) &v6)) + ac->authority_info.dns_authority.found = GNUNET_YES; + break; + } default: break; } - if (NULL != sa) - break; - } - if (NULL == sa) - { - /* we cannot continue; NS without A/AAAA */ - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* expand authority chain */ - ac = GNUNET_new (struct AuthorityChain); - ac->rh = rh; - GNUNET_assert (strlen (rh->g2dc->ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH); - strcpy (ac->authority_info.dns_authority.name, - rh->g2dc->ns); - GNUNET_memcpy (&ac->authority_info.dns_authority.dns_ip, - sa, - sa_len); - /* for DNS recursion, the label is the full DNS name, - created from the remainder of the GNS name and the - name in the NS record */ - GNUNET_asprintf (&ac->label, - "%.*s%s%s", - (int) rh->name_resolution_pos, - rh->name, - (0 != rh->name_resolution_pos) ? "." : "", - rh->g2dc->ns); - GNUNET_free (rh->g2dc->ns); - GNUNET_free (rh->g2dc); - rh->g2dc = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Will continue resolution using DNS server `%s' to resolve `%s'\n", - GNUNET_a2s (sa, - sa_len), - ac->label); - GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, - rh->ac_tail, - ac); - if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("GNS lookup resulted in DNS name that is too long (`%s')\n"), - ac->label); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; } - /* recurse */ - rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, - rh); + continue_with_gns2dns (ac); } /** * Function called by the resolver for each address obtained from DNS. * - * @param cls closure, a `struct Gns2DnsContext *` + * @param cls closure, a `struct Gns2DnsPending *` * @param addr one of the addresses of the host, NULL for the last address * @param addrlen length of @a addr */ @@ -1448,57 +1492,270 @@ handle_gns2dns_ip (void *cls, const struct sockaddr *addr, socklen_t addrlen) { - struct Gns2DnsContext *g2dc = cls; - struct GNUNET_GNSRECORD_Data rd; - + struct Gns2DnsPending *gp = cls; + struct AuthorityChain *ac = gp->ac; + + GNUNET_RESOLVER_request_cancel (gp->dns_rh); + GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, + ac->authority_info.dns_authority.gp_tail, + gp); + GNUNET_free (gp); if (NULL == addr) { /* DNS resolution failed */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to use DNS to resolve name of DNS resolver\n"); - g2dc->rh->g2dc = NULL; - fail_resolution (g2dc->rh); - GNUNET_free (g2dc); } - switch (addr->sa_family) + else { - case AF_INET: + if (GNUNET_OK == + GNUNET_DNSSTUB_add_dns_sa (ac->authority_info.dns_authority.dns_handle, + addr)) + ac->authority_info.dns_authority.found = GNUNET_YES; + } + continue_with_gns2dns (ac); +} + + +/** + * We found a CNAME record, perform recursive resolution on it. + * + * @param rh resolution handle + * @param rd record with CNAME to resolve recursively + */ +static void +recursive_cname_resolution (struct GNS_ResolverHandle *rh, + const struct GNUNET_GNSRECORD_Data *rd) +{ + char *cname; + size_t off; + + off = 0; + cname = GNUNET_DNSPARSER_parse_name (rd->data, + rd->data_size, + &off); + if ( (NULL == cname) || + (off != rd->data_size) ) + { + GNUNET_break_op (0); /* record not well-formed */ + GNUNET_free_non_null (cname); + fail_resolution (rh); + return; + } + handle_gns_cname_result (rh, + cname); + GNUNET_free (cname); +} + + +/** + * We found a PKEY record, perform recursive resolution on it. + * + * @param rh resolution handle + * @param rd record with PKEY to resolve recursively + */ +static void +recursive_pkey_resolution (struct GNS_ResolverHandle *rh, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct AuthorityChain *ac; + + /* delegation to another zone */ + if (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) != + rd->data_size) + { + GNUNET_break_op (0); + fail_resolution (rh); + return; + } + /* expand authority chain */ + ac = GNUNET_new (struct AuthorityChain); + ac->rh = rh; + ac->gns_authority = GNUNET_YES; + ac->suggested_shortening_label = NULL; + ac->shortening_started = GNUNET_NO; + GNUNET_memcpy (&ac->authority_info.gns_authority, + rd->data, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + ac->label = resolver_lookup_get_next_label (rh); + /* add AC to tail */ + GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, + rh->ac_tail, + ac); + /* recurse */ + rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, + rh); +} + + +/** + * We found one or more GNS2DNS records, perform recursive resolution on it. + * (to be precise, one or more records in @a rd is GNS2DNS, there may be others, + * so this function still needs to check which ones are GNS2DNS). + * + * @param rh resolution handle + * @param rd_count length of the @a rd array + * @param rd record with PKEY to resolve recursively + * @return #GNUNET_OK if this worked, #GNUNET_SYSERR if no GNS2DNS records were in @a rd + */ +static int +recursive_gns2dns_resolution (struct GNS_ResolverHandle *rh, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct AuthorityChain *ac; + const char *tld; + char *ns; + + ns = NULL; + /* expand authority chain */ + ac = GNUNET_new (struct AuthorityChain); + ac->rh = rh; + ac->authority_info.dns_authority.dns_handle = GNUNET_DNSSTUB_start (4); + + for (unsigned int i=0;idns_rh); - g2dc->dns_rh = NULL; - handle_gns2dns_result (g2dc->rh, - 1, - &rd); + /* check if 'ip' is already an IPv4/IPv6 address */ + if (GNUNET_OK == + GNUNET_DNSSTUB_add_dns_ip (ac->authority_info.dns_authority.dns_handle, + ip)) + { + ac->authority_info.dns_authority.found = GNUNET_YES; + GNUNET_free (ip); + continue; + } + tld = GNS_get_tld (ip); + if (0 != strcmp (tld, + "+")) + { + /* 'ip' is a DNS name */ + gp = GNUNET_new (struct Gns2DnsPending); + gp->ac = ac; + GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head, + ac->authority_info.dns_authority.gp_tail, + gp); + gp->dns_rh = GNUNET_RESOLVER_ip_get (ip, + AF_UNSPEC, + GNUNET_TIME_UNIT_FOREVER_REL, + &handle_gns2dns_ip, + gp); + GNUNET_free (ip); + continue; + } + /* 'ip' should be a GNS name */ + gp = GNUNET_new (struct Gns2DnsPending); + gp->ac = ac; + GNUNET_CONTAINER_DLL_insert (ac->authority_info.dns_authority.gp_head, + ac->authority_info.dns_authority.gp_tail, + gp); + gp->rh = GNUNET_new (struct GNS_ResolverHandle); + ip = translate_dot_plus (rh, + ip); + tld = GNS_get_tld (ip); + if (GNUNET_OK != + GNUNET_GNSRECORD_zkey_to_pkey (tld, + &zone)) + { + GNUNET_break_op (0); + GNUNET_free (ip); + continue; + } + gp->rh->authority_zone = zone; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resolving `%s' to determine IP address of DNS server for GNS2DNS transition for `%s'\n", + ip, + ns); + gp->rh->name = ip; + gp->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1; + gp->rh->proc = &handle_gns2dns_result; + gp->rh->proc_cls = gp; + gp->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY; + gp->rh->options = GNUNET_GNS_LO_DEFAULT; + gp->rh->loop_limiter = rh->loop_limiter + 1; + gp->rh->task_id + = GNUNET_SCHEDULER_add_now (&start_resolver_lookup, + gp->rh); + } /* end 'for all records' */ + + if (NULL == ns) + { + /* not a single GNS2DNS record found */ + GNUNET_free (ac); + return GNUNET_SYSERR; + } + GNUNET_assert (strlen (ns) <= GNUNET_DNSPARSER_MAX_NAME_LENGTH); + strcpy (ac->authority_info.dns_authority.name, + ns); + /* for DNS recursion, the label is the full DNS name, + created from the remainder of the GNS name and the + name in the NS record */ + GNUNET_asprintf (&ac->label, + "%.*s%s%s", + (int) rh->name_resolution_pos, + rh->name, + (0 != rh->name_resolution_pos) ? "." : "", + ns); + GNUNET_free (ns); + GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, + rh->ac_tail, + ac); + if (strlen (ac->label) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("GNS lookup resulted in DNS name that is too long (`%s')\n"), + ac->label); + return GNUNET_SYSERR; + } + continue_with_gns2dns (ac); + return GNUNET_OK; } - /** * Process a records that were decrypted from a block. * @@ -1512,7 +1769,6 @@ handle_gns_resolution_result (void *cls, const struct GNUNET_GNSRECORD_Data *rd) { struct GNS_ResolverHandle *rh = cls; - struct AuthorityChain *ac; struct AuthorityChain *shorten_ac; char *cname; struct VpnContext *vpn_ctx; @@ -1532,6 +1788,14 @@ handle_gns_resolution_result (void *cls, rh->ac_tail->label, GNUNET_GNSRECORD_z2s (&rh->ac_tail->authority_info.gns_authority), rd_count); + if (0 == rd_count) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("GNS lookup failed (zero records found)\n")); + fail_resolution (rh); + return; + } + if (0 == rh->name_resolution_pos) { /* top-level match, are we done yet? */ @@ -1547,9 +1811,8 @@ handle_gns_resolution_result (void *cls, (off != rd[0].data_size) ) { GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); GNUNET_free_non_null (cname); + fail_resolution (rh); return; } handle_gns_cname_result (rh, @@ -1573,8 +1836,7 @@ handle_gns_resolution_result (void *cls, rd[i].data_size) { GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } vpn = (const struct GNUNET_TUN_GnsVpnRecord *) rd[i].data; @@ -1582,8 +1844,7 @@ handle_gns_resolution_result (void *cls, if ('\0' != vname[rd[i].data_size - 1 - sizeof (struct GNUNET_TUN_GnsVpnRecord)]) { GNUNET_break_op (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } GNUNET_TUN_service_name_to_hash (vname, @@ -1621,7 +1882,13 @@ handle_gns_resolution_result (void *cls, /* delegation to DNS */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found GNS2DNS record, delegating to DNS!\n"); - goto do_recurse; + if (GNUNET_OK == + recursive_gns2dns_resolution (rh, + rd_count, + rd)) + return; + else + goto fail; } default: break; @@ -1823,7 +2090,9 @@ handle_gns_resolution_result (void *cls, GNUNET_break_op (0); break; } - GNUNET_memcpy (&pub, rd[i].data, rd[i].data_size); + GNUNET_memcpy (&pub, + rd[i].data, + rd[i].data_size); rd_off++; if (GNUNET_GNSRECORD_TYPE_PKEY != rh->record_type) { @@ -1856,7 +2125,13 @@ handle_gns_resolution_result (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found GNS2DNS record, delegating to DNS!\n"); - goto do_recurse; + if (GNUNET_OK == + recursive_gns2dns_resolution (rh, + rd_count, + rd)) + return; + else + goto fail; } case GNUNET_GNSRECORD_TYPE_BOX: { @@ -1901,200 +2176,35 @@ handle_gns_resolution_result (void *cls, rh->proc (rh->proc_cls, rd_off, rd_new); - GNS_resolver_lookup_cancel (rh); + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); return; } - do_recurse: - /* need to recurse, check if we can */ - for (unsigned int i=0;iproc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - return; - } - /* expand authority chain */ - ac = GNUNET_new (struct AuthorityChain); - ac->rh = rh; - ac->gns_authority = GNUNET_YES; - ac->suggested_shortening_label = NULL; - ac->shortening_started = GNUNET_NO; - GNUNET_memcpy (&ac->authority_info.gns_authority, - rd[i].data, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - ac->label = resolver_lookup_get_next_label (rh); - /* add AC to tail */ - GNUNET_CONTAINER_DLL_insert_tail (rh->ac_head, - rh->ac_tail, - ac); - /* recurse */ - rh->task_id = GNUNET_SCHEDULER_add_now (&recursive_resolution, - rh); + case GNUNET_DNSPARSER_TYPE_CNAME: + GNUNET_break_op (1 == rd_count); /* CNAME should be unique */ + recursive_cname_resolution (rh, + &rd[0]); + return; + case GNUNET_GNSRECORD_TYPE_PKEY: + GNUNET_break_op (1 == rd_count); /* PKEY should be unique */ + recursive_pkey_resolution (rh, + &rd[0]); + return; + default: + if (GNUNET_OK == + recursive_gns2dns_resolution (rh, + rd_count, + rd)) return; - case GNUNET_GNSRECORD_TYPE_GNS2DNS: - { - /* TODO: Right now we only foward the request to ONE DNS resolver, - even if we get multiple IP addresses back; a correct implementation - should try all DNS resolvers. */ - /* resolution continues within DNS */ - struct Gns2DnsContext *g2dc; - char *ip; - char *ns; - const char *tld; - struct GNUNET_CRYPTO_EcdsaPublicKey zone; - struct in_addr v4; - struct in6_addr v6; - - off = 0; - ns = GNUNET_DNSPARSER_parse_name (rd[i].data, - rd[i].data_size, - &off); - ip = GNUNET_DNSPARSER_parse_name (rd[i].data, - rd[i].data_size, - &off); - if ( (NULL == ns) || - (NULL == ip) || - (off != rd[i].data_size) ) - { - GNUNET_break_op (0); - GNUNET_free_non_null (ns); - GNUNET_free_non_null (ip); - fail_resolution (rh); - return; - } - /* resolve 'ip' to determine the IP(s) of the DNS - resolver to use for lookup of 'ns' */ - g2dc = GNUNET_new (struct Gns2DnsContext); - g2dc->ns = ns; - rh->g2dc = g2dc; - - /* check if 'ip' is already an IPv4/IPv6 address */ - if (1 == inet_pton (AF_INET, - ip, - &v4)) - { - /* name is IPv4 address, pretend it's an A record */ - struct GNUNET_GNSRECORD_Data rd; - - GNUNET_free (ip); - rd.data = &v4; - rd.data_size = sizeof (v4); - rd.expiration_time = UINT64_MAX; - rd.record_type = GNUNET_DNSPARSER_TYPE_A; - rd.flags = 0; - handle_gns2dns_result (rh, - 1, - &rd); - return; - } - if (1 == inet_pton (AF_INET6, - ip, - &v6)) - { - /* name is IPv6 address, pretend it's an AAAA record */ - struct GNUNET_GNSRECORD_Data rd; - - GNUNET_free (ip); - rd.data = &v6; - rd.data_size = sizeof (v6); - rd.expiration_time = UINT64_MAX; - rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA; - rd.flags = 0; - handle_gns2dns_result (rh, - 1, - &rd); - return; - } - - tld = GNS_get_tld (ip); - if (0 != strcmp (tld, - "+")) - { - /* 'ip' is a DNS name */ - g2dc->dns_rh = GNUNET_RESOLVER_ip_get (ip, - AF_UNSPEC, - GNUNET_TIME_UNIT_FOREVER_REL, - &handle_gns2dns_ip, - g2dc); - GNUNET_free (ip); - return; - } - - /* 'ip' should be a GNS name */ - g2dc->rh = GNUNET_new (struct GNS_ResolverHandle); - - ip = translate_dot_plus (rh, - ip); - tld = GNS_get_tld (ip); - if (GNUNET_OK != - GNUNET_GNSRECORD_zkey_to_pkey (tld, - &zone)) - { - GNUNET_break_op (0); - GNUNET_free_non_null (ns); - GNUNET_free_non_null (ip); - GNUNET_free (g2dc); - fail_resolution (rh); - return; - } - g2dc->rh->authority_zone = zone; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resolving `%s' to determine IP address of DNS server for GNS2DNS transition for `%s'\n", - ip, - ns); - g2dc->rh->name = ip; - g2dc->rh->name_resolution_pos = strlen (ip) - strlen (tld) - 1; - g2dc->rh->proc = &handle_gns2dns_result; - g2dc->rh->proc_cls = rh; - g2dc->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY; - g2dc->rh->options = GNUNET_GNS_LO_DEFAULT; - g2dc->rh->loop_limiter = rh->loop_limiter + 1; - g2dc->rh->task_id - = GNUNET_SCHEDULER_add_now (&start_resolver_lookup, - g2dc->rh); - return; - } - case GNUNET_DNSPARSER_TYPE_CNAME: - { - char *cname; - - off = 0; - cname = GNUNET_DNSPARSER_parse_name (rd[i].data, - rd[i].data_size, - &off); - if ( (NULL == cname) || - (off != rd[i].data_size) ) - { - GNUNET_break_op (0); /* record not well-formed */ - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); - GNUNET_free_non_null (cname); - return; - } - handle_gns_cname_result (rh, - cname); - GNUNET_free (cname); - return; - } - /* FIXME: handle DNAME */ - default: - /* skip */ - break; - } + break; } + fail: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("GNS lookup recursion failed (no delegation record found)\n")); - rh->proc (rh->proc_cls, - 0, - NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); } @@ -2176,8 +2286,7 @@ handle_dht_response (void *cls, { /* how did this pass DHT block validation!? */ GNUNET_break (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } block = data; @@ -2188,8 +2297,7 @@ handle_dht_response (void *cls, { /* how did this pass DHT block validation!? */ GNUNET_break (0); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } if (GNUNET_OK != @@ -2200,8 +2308,7 @@ handle_dht_response (void *cls, rh)) { GNUNET_break_op (0); /* block was ill-formed */ - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) @@ -2252,8 +2359,7 @@ start_dht_request (struct GNS_ResolverHandle *rh, /* fail longest-standing DHT request */ rx = GNUNET_CONTAINER_heap_peek (dht_lookup_heap); GNUNET_assert (NULL != rx); - rx->proc (rx->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rx); + fail_resolution (rx); } } @@ -2324,8 +2430,7 @@ handle_namecache_block_response (void *cls, "Resolution failed for `%s' in zone %s (DHT lookup not permitted by configuration)\n", ac->label, GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -2408,8 +2513,7 @@ handle_revocation_result (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Zone %s was revoked, resolution fails\n"), GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority)); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } recursive_gns_resolution_namecache (rh); @@ -2453,8 +2557,7 @@ recursive_resolution (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Encountered unbounded recursion resolving `%s'\n", rh->name); - rh->proc (rh->proc_cls, 0, NULL); - GNS_resolver_lookup_cancel (rh); + fail_resolution (rh); return; } if (GNUNET_YES == rh->ac_tail->gns_authority) @@ -2491,8 +2594,12 @@ start_resolver_lookup (void *cls) rd.expiration_time = UINT64_MAX; rd.record_type = GNUNET_DNSPARSER_TYPE_A; rd.flags = 0; - rh->proc (rh->proc_cls, 1, &rd); - GNS_resolver_lookup_cancel (rh); + rh->proc (rh->proc_cls, + 1, + &rd); + GNUNET_assert (NULL == rh->task_id); + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); return; } if (1 == inet_pton (AF_INET6, @@ -2507,8 +2614,12 @@ start_resolver_lookup (void *cls) rd.expiration_time = UINT64_MAX; rd.record_type = GNUNET_DNSPARSER_TYPE_AAAA; rd.flags = 0; - rh->proc (rh->proc_cls, 1, &rd); - GNS_resolver_lookup_cancel (rh); + rh->proc (rh->proc_cls, + 1, + &rd); + GNUNET_assert (NULL == rh->task_id); + rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + rh); return; } @@ -2587,37 +2698,51 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) GNUNET_CONTAINER_DLL_remove (rlh_head, rlh_tail, rh); + if (NULL != rh->dns_request) + { + GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); + rh->dns_request = NULL; + } while (NULL != (ac = rh->ac_head)) { GNUNET_CONTAINER_DLL_remove (rh->ac_head, rh->ac_tail, ac); + if (GNUNET_NO == ac->gns_authority) + { + struct Gns2DnsPending *gp; + + while (NULL != (gp = ac->authority_info.dns_authority.gp_head)) + { + GNUNET_CONTAINER_DLL_remove (ac->authority_info.dns_authority.gp_head, + ac->authority_info.dns_authority.gp_tail, + gp); + if (NULL != gp->rh) + { + /* rh->g2dc->rh is NOT in the DLL yet, so to enable us + using GNS_resolver_lookup_cancel here, we need to + add it first... */ + GNUNET_CONTAINER_DLL_insert (rlh_head, + rlh_tail, + gp->rh); + GNUNET_assert (NULL == gp->rh->task_id); + gp->rh->task_id = GNUNET_SCHEDULER_add_now (&GNS_resolver_lookup_cancel_, + gp->rh); + gp->rh = NULL; + } + if (NULL != gp->dns_rh) + { + GNUNET_RESOLVER_request_cancel (gp->dns_rh); + gp->dns_rh = NULL; + } + GNUNET_free (gp); + } + GNUNET_DNSSTUB_stop (ac->authority_info.dns_authority.dns_handle); + } GNUNET_free (ac->label); GNUNET_free_non_null (ac->suggested_shortening_label); GNUNET_free (ac); } - if (NULL != rh->g2dc) - { - /* rh->g2dc->rh is NOT in the DLL yet, so to enable us - using GNS_resolver_lookup_cancel here, we need to - add it first... */ - if (NULL != rh->g2dc->rh) - { - GNUNET_CONTAINER_DLL_insert (rlh_head, - rlh_tail, - rh->g2dc->rh); - GNS_resolver_lookup_cancel (rh->g2dc->rh); - rh->g2dc->rh = NULL; - } - if (NULL != rh->g2dc->dns_rh) - { - GNUNET_RESOLVER_request_cancel (rh->g2dc->dns_rh); - rh->g2dc->rh = NULL; - } - GNUNET_free (rh->g2dc->ns); - GNUNET_free (rh->g2dc); - rh->g2dc = NULL; - } if (NULL != rh->task_id) { GNUNET_SCHEDULER_cancel (rh->task_id); @@ -2639,11 +2764,6 @@ GNS_resolver_lookup_cancel (struct GNS_ResolverHandle *rh) GNUNET_free (vpn_ctx->rd_data); GNUNET_free (vpn_ctx); } - if (NULL != rh->dns_request) - { - GNUNET_DNSSTUB_resolve_cancel (rh->dns_request); - rh->dns_request = NULL; - } if (NULL != rh->namecache_qe) { GNUNET_NAMECACHE_cancel (rh->namecache_qe); @@ -2690,8 +2810,6 @@ GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc, const struct GNUNET_CONFIGURATION_Handle *c, unsigned long long max_bg_queries) { - char *dns_ip; - cfg = c; namecache_handle = nc; dht_handle = dht; @@ -2706,18 +2824,6 @@ GNS_resolver_init (struct GNUNET_NAMECACHE_Handle *nc, if (GNUNET_NO == use_cache) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Namecache disabled\n"); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (c, - "gns", - "DNS_RESOLVER", - &dns_ip)) - { - /* user did not specify DNS resolver, use 8.8.8.8 */ - dns_ip = GNUNET_strdup ("8.8.8.8"); - } - dns_handle = GNUNET_DNSSTUB_start (dns_ip); - GNUNET_free (dns_ip); vpn_handle = GNUNET_VPN_connect (cfg); } @@ -2734,7 +2840,9 @@ GNS_resolver_done () /* abort active resolutions */ while (NULL != (rh = rlh_head)) { - rh->proc (rh->proc_cls, 0, NULL); + rh->proc (rh->proc_cls, + 0, + NULL); GNS_resolver_lookup_cancel (rh); } while (NULL != (co = co_head)) @@ -2747,8 +2855,6 @@ GNS_resolver_done () } GNUNET_CONTAINER_heap_destroy (dht_lookup_heap); dht_lookup_heap = NULL; - GNUNET_DNSSTUB_stop (dns_handle); - dns_handle = NULL; GNUNET_VPN_disconnect (vpn_handle); vpn_handle = NULL; dht_handle = NULL; diff --git a/src/gns/test_gns_lookup.conf b/src/gns/test_gns_lookup.conf index ea8c7c3fc..a9a2345c7 100644 --- a/src/gns/test_gns_lookup.conf +++ b/src/gns/test_gns_lookup.conf @@ -10,7 +10,7 @@ AUTOSTART = YES PLUGINS = [gns] -#PREFIX = valgrind --leak-check=full --track-origins=yes +# PREFIX = valgrind --leak-check=full --track-origins=yes AUTOSTART = YES AUTO_IMPORT_PKEY = YES MAX_PARALLEL_BACKGROUND_QUERIES = 10 diff --git a/src/include/gnunet_dnsstub_lib.h b/src/include/gnunet_dnsstub_lib.h index 1c3305f7b..41e30d044 100644 --- a/src/include/gnunet_dnsstub_lib.h +++ b/src/include/gnunet_dnsstub_lib.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2012 GNUnet e.V. + Copyright (C) 2012, 2018 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 @@ -48,13 +48,51 @@ struct GNUNET_DNSSTUB_RequestSocket; /** * Start a DNS stub resolver. * - * @param dns_ip target IP address to use + * @param num_sockets how many sockets should we open + * in parallel for DNS queries for this stub? * @return NULL on error */ struct GNUNET_DNSSTUB_Context * -GNUNET_DNSSTUB_start (const char *dns_ip); +GNUNET_DNSSTUB_start (unsigned int num_sockets); +/** + * Add nameserver for use by the DNSSTUB. We will use + * all provided nameservers for resolution (round-robin). + * + * @param ctx resolver context to modify + * @param dns_ip target IP address to use (as string) + * @return #GNUNET_OK on success + */ +int +GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx, + const char *dns_ip); + + +/** + * Add nameserver for use by the DNSSTUB. We will use + * all provided nameservers for resolution (round-robin). + * + * @param ctx resolver context to modify + * @param sa socket address of DNS resolver to use + * @return #GNUNET_OK on success + */ +int +GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx, + const struct sockaddr *sa); + + +/** + * How long should we try requests before timing out? + * Only effective for requests issued after this call. + * + * @param ctx resolver context to modify + * @param retry_frequ how long to wait between retries + */ +void +GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx, + struct GNUNET_TIME_Relative retry_freq); + /** * Cleanup DNSSTUB resolver. * @@ -66,57 +104,36 @@ GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx); /** * Function called with the result of a DNS resolution. + * Once this function is called, the resolution request + * is automatically cancelled / cleaned up. In particular, + * the function will only be called once. * * @param cls closure - * @param rs socket that received the response - * @param dns dns response, never NULL + * @param dns dns response, NULL on hard error (i.e. timeout) * @param dns_len number of bytes in @a dns */ typedef void (*GNUNET_DNSSTUB_ResultCallback)(void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t dns_len); -/** - * Perform DNS resolution using given address. - * - * @param ctx stub resolver to use - * @param sa the socket address - * @param sa_len the socket length - * @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 @a 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); - - /** * 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 function to call with result (once) * @param rc_cls closure for @a 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); +GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx, + const void *request, + size_t request_len, + GNUNET_DNSSTUB_ResultCallback rc, + void *rc_cls); /** diff --git a/src/namestore/gnunet-zoneimport.c b/src/namestore/gnunet-zoneimport.c index 4f4151c94..279bfddea 100644 --- a/src/namestore/gnunet-zoneimport.c +++ b/src/namestore/gnunet-zoneimport.c @@ -818,13 +818,11 @@ store_completed_cb (void *cls, * Function called with the result of a DNS resolution. * * @param cls closure with the `struct Request` - * @param rs socket that received the response * @param dns dns response, never NULL * @param dns_len number of bytes in @a dns */ static void process_result (void *cls, - struct GNUNET_DNSSTUB_RequestSocket *rs, const struct GNUNET_TUN_DnsHeader *dns, size_t dns_len) { @@ -833,7 +831,6 @@ process_result (void *cls, struct GNUNET_DNSPARSER_Packet *p; unsigned int rd_count; - (void) rs; GNUNET_assert (NULL == req->hn); if (NULL == dns) { @@ -970,11 +967,11 @@ submit_req (struct Request *req) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting resolution for `%s'\n", req->hostname); - req->rs = GNUNET_DNSSTUB_resolve2 (ctx, - req->raw, - req->raw_len, - &process_result, - req); + req->rs = GNUNET_DNSSTUB_resolve (ctx, + req->raw, + req->raw_len, + &process_result, + req); GNUNET_assert (NULL != req->rs); req->issue_num++; last_request = now; @@ -1396,13 +1393,33 @@ run (void *cls, (void) args; (void) cfgfile; req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - ctx = GNUNET_DNSSTUB_start (dns_server); + ctx = GNUNET_DNSSTUB_start (256); if (NULL == ctx) { fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n"); return; } + if (NULL == args[1]) + { + fprintf (stderr, + "You must provide a list of DNS resolvers on the command line\n"); + return; + } + for (unsigned int i=1;NULL != args[i];i++) + { + if (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (ctx, + args[1])) + { + fprintf (stderr, + "Failed to use `%s' for DNS resolver\n", + args[i]); + return; + } + } + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); ns = GNUNET_NAMESTORE_connect (cfg); -- 2.25.1