From a51ba5bbff8a583aa5922b43a46121414c97e8aa Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 15 Mar 2021 15:58:51 -0400 Subject: [PATCH 1/5] daemon/ns: parse --upstream option and REFUSE non-HNS queries --- src/daemon.c | 25 ++++++++++++++++++++++-- src/error.c | 1 + src/error.h | 5 ++++- src/ns.c | 52 +++++++++++++++++++++++++++++++++++++++----------- src/ns.h | 14 ++++++++++++-- src/resource.c | 12 ++++++++++++ src/resource.h | 3 +++ 7 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/daemon.c b/src/daemon.c index f11dd564..ee74207e 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -36,6 +36,8 @@ typedef struct hsk_options_s { uint8_t *identity_key; char *seeds; int pool_size; + char *upstream; + } hsk_options_t; static void @@ -52,6 +54,7 @@ hsk_options_init(hsk_options_t *opt) { opt->identity_key = NULL; opt->seeds = NULL; opt->pool_size = HSK_POOL_SIZE; + opt->upstream = NULL; } static void @@ -141,6 +144,11 @@ help(int r) { " -l, --log-file \n" " Redirect output to a log file.\n" "\n" + " -t, --upstream \n" + " IP address and port to forward queries that fail HNS lookup.\n" + " Example:\n" + " -t 1.1.1.1\n" + "\n" #ifndef _WIN32 " -d, --daemon\n" " Fork and background the process.\n" @@ -156,7 +164,7 @@ help(int r) { static void parse_arg(int argc, char **argv, hsk_options_t *opt) { - const static char *optstring = "c:n:r:i:u:p:k:s:l:h" + const static char *optstring = "c:n:r:i:u:p:k:s:l:t:h" #ifndef _WIN32 "d" #endif @@ -172,6 +180,7 @@ parse_arg(int argc, char **argv, hsk_options_t *opt) { { "identity-key", required_argument, NULL, 'k' }, { "seeds", required_argument, NULL, 's' }, { "log-file", required_argument, NULL, 'l' }, + { "upstream", required_argument, NULL, 't' }, #ifndef _WIN32 { "daemon", no_argument, NULL, 'd' }, #endif @@ -307,6 +316,18 @@ parse_arg(int argc, char **argv, hsk_options_t *opt) { break; } + case 't': { + if (!optarg || strlen(optarg) == 0) + return help(1); + + if (opt->upstream) + free(opt->upstream); + + opt->upstream = strdup(optarg); + + break; + } + #ifndef _WIN32 case 'd': { background = true; @@ -428,7 +449,7 @@ hsk_daemon_init(hsk_daemon_t *daemon, uv_loop_t *loop, hsk_options_t *opt) { goto fail; } - daemon->ns = hsk_ns_alloc(loop, daemon->pool); + daemon->ns = hsk_ns_alloc(loop, daemon->pool, (bool)opt->upstream); if (!daemon->ns) { fprintf(stderr, "failed initializing ns\n"); diff --git a/src/error.c b/src/error.c index 0fcf10cf..5c916c70 100644 --- a/src/error.c +++ b/src/error.c @@ -38,6 +38,7 @@ static const char *errstrs[] = { "EACTTHREE", "EBADSIZE", "EBADTAG", + "EREFUSED", "EUNKNOWN" }; diff --git a/src/error.h b/src/error.h index be8783b4..f8c6b93a 100644 --- a/src/error.h +++ b/src/error.h @@ -45,8 +45,11 @@ #define HSK_EBADSIZE 30 #define HSK_EBADTAG 31 +// DNS +#define HSK_EREFUSED 32 + // Max -#define HSK_MAXERROR 32 +#define HSK_MAXERROR 33 const char * hsk_strerror(int code); diff --git a/src/ns.c b/src/ns.c index 6da6499f..9fffbf1c 100644 --- a/src/ns.c +++ b/src/ns.c @@ -100,7 +100,12 @@ hsk_icann_lookup(const char *name); */ int -hsk_ns_init(hsk_ns_t *ns, const uv_loop_t *loop, const hsk_pool_t *pool) { +hsk_ns_init( + hsk_ns_t *ns, + const uv_loop_t *loop, + const hsk_pool_t *pool, + const bool upstream +) { if (!ns || !loop || !pool) return HSK_EBADARGS; @@ -121,6 +126,7 @@ hsk_ns_init(hsk_ns_t *ns, const uv_loop_t *loop, const hsk_pool_t *pool) { memset(ns->pubkey, 0x00, sizeof(ns->pubkey)); memset(ns->read_buffer, 0x00, sizeof(ns->read_buffer)); ns->receiving = false; + ns->upstream = upstream; return HSK_SUCCESS; } @@ -241,13 +247,17 @@ hsk_ns_close(hsk_ns_t *ns) { } hsk_ns_t * -hsk_ns_alloc(const uv_loop_t *loop, const hsk_pool_t *pool) { +hsk_ns_alloc( + const uv_loop_t *loop, + const hsk_pool_t *pool, + const bool upstream +) { hsk_ns_t *ns = malloc(sizeof(hsk_ns_t)); if (!ns) return NULL; - if (hsk_ns_init(ns, loop, pool) != HSK_SUCCESS) { + if (hsk_ns_init(ns, loop, pool, upstream) != HSK_SUCCESS) { free(ns); return NULL; } @@ -511,6 +521,16 @@ hsk_ns_respond( if (status != HSK_SUCCESS) { // Pool resolve error. hsk_ns_log(ns, "resolve response error: %s\n", hsk_strerror(status)); + + // Forward to custom upstream resolver + if (status == HSK_EREFUSED) { + msg = hsk_resource_to_refused(); + + if (!msg) + hsk_ns_log(ns, "could not create refused response (%u)\n", req->id); + else + hsk_ns_log(ns, "sending refused (%u)\n", req->id); + } } else if (!res) { // Doesn't exist. // @@ -726,16 +746,26 @@ after_resolve( if (status == HSK_SUCCESS) { if (!exists || data_len == 0) { - const uint8_t *item = hsk_icann_lookup(name); + if (ns->upstream) { + // User has requested that all non-HNS queries + // get forwarded to a specific upstream resolver. + // Instead of sending NXDOMAIN we respond to the recursive + // with a REFUSED error as a special cue. + hsk_ns_log(ns, "forwarding to upstream resolver: %s\n", name); + status = HSK_EREFUSED; + res = NULL; + } else { + const uint8_t *item = hsk_icann_lookup(name); - if (item) { - const uint8_t *raw = &item[2]; - size_t raw_len = (((size_t)item[1]) << 8) | ((size_t)item[0]); + if (item) { + const uint8_t *raw = &item[2]; + size_t raw_len = (((size_t)item[1]) << 8) | ((size_t)item[0]); - if (!hsk_resource_decode(raw, raw_len, &res)) { - hsk_ns_log(ns, "could not decode root resource for: %s\n", name); - status = HSK_EFAILURE; - res = NULL; + if (!hsk_resource_decode(raw, raw_len, &res)) { + hsk_ns_log(ns, "could not decode root resource for: %s\n", name); + status = HSK_EFAILURE; + res = NULL; + } } } } else { diff --git a/src/ns.h b/src/ns.h index b9d1797d..9dfc3c07 100644 --- a/src/ns.h +++ b/src/ns.h @@ -33,6 +33,7 @@ typedef struct { uint8_t pubkey[33]; uint8_t read_buffer[HSK_UDP_BUFFER]; bool receiving; + bool upstream; } hsk_ns_t; /* @@ -40,7 +41,12 @@ typedef struct { */ int -hsk_ns_init(hsk_ns_t *ns, const uv_loop_t *loop, const hsk_pool_t *pool); +hsk_ns_init( + hsk_ns_t *ns, + const uv_loop_t *loop, + const hsk_pool_t *pool, + const bool upstream +); void hsk_ns_uninit(hsk_ns_t *ns); @@ -58,7 +64,11 @@ int hsk_ns_close(hsk_ns_t *ns); hsk_ns_t * -hsk_ns_alloc(const uv_loop_t *loop, const hsk_pool_t *pool); +hsk_ns_alloc( + const uv_loop_t *loop, + const hsk_pool_t *pool, + const bool upstream +); void hsk_ns_free(hsk_ns_t *ns); diff --git a/src/resource.c b/src/resource.c index a79dd202..8d33271a 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1113,6 +1113,18 @@ hsk_resource_to_notimp(void) { return msg; } +hsk_dns_msg_t * +hsk_resource_to_refused(void) { + hsk_dns_msg_t *msg = hsk_dns_msg_alloc(); + + if (!msg) + return NULL; + + msg->code = HSK_DNS_REFUSED; + + return msg; +} + /* * Helpers */ diff --git a/src/resource.h b/src/resource.h index 46e73b1f..a2b66236 100644 --- a/src/resource.h +++ b/src/resource.h @@ -91,6 +91,9 @@ hsk_resource_to_servfail(void); hsk_dns_msg_t * hsk_resource_to_notimp(void); +hsk_dns_msg_t * +hsk_resource_to_refused(void); + bool hsk_resource_is_ptr(const char *name); From f72ef6cde384ec2d67817b9f99cf69798e0242c9 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 17 Mar 2021 13:25:08 -0400 Subject: [PATCH 2/5] rs: parse --upstream and create second unbound context for fallback --- src/daemon.c | 2 +- src/rs.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/rs.h | 16 +++++++-- 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/daemon.c b/src/daemon.c index ee74207e..51bda512 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -471,7 +471,7 @@ hsk_daemon_init(hsk_daemon_t *daemon, uv_loop_t *loop, hsk_options_t *opt) { } } - daemon->rs = hsk_rs_alloc(loop, opt->ns_host); + daemon->rs = hsk_rs_alloc(loop, opt->ns_host, opt->upstream); if (!daemon->rs) { fprintf(stderr, "failed initializing rns\n"); diff --git a/src/rs.c b/src/rs.c index 3794190d..036c5fa4 100644 --- a/src/rs.c +++ b/src/rs.c @@ -75,7 +75,12 @@ after_resolve(void *data, int status, struct ub_result *result); */ int -hsk_rs_init(hsk_rs_t *ns, const uv_loop_t *loop, const struct sockaddr *stub) { +hsk_rs_init( + hsk_rs_t *ns, + const uv_loop_t *loop, + const struct sockaddr *stub, + const char *upstream +) { if (!ns || !loop) return HSK_EBADARGS; @@ -110,6 +115,9 @@ hsk_rs_init(hsk_rs_t *ns, const uv_loop_t *loop, const struct sockaddr *stub) { memset(ns->read_buffer, 0x00, sizeof(ns->read_buffer)); ns->receiving = false; ns->stop_callback = NULL; + ns->fallback = NULL; + ns->fallback_worker = NULL; + ns->upstream = (char *)upstream; if (stub) { err = HSK_EFAILURE; @@ -121,6 +129,22 @@ hsk_rs_init(hsk_rs_t *ns, const uv_loop_t *loop, const struct sockaddr *stub) { goto fail; } + if (upstream) { + err = HSK_EFAILURE; + + struct ub_ctx *fallback = NULL; + + fallback = ub_ctx_create(); + + if (!fallback) + goto fail; + + if (ub_ctx_async(fallback, 1) != 0) + goto fail; + + ns->fallback = fallback; + } + return HSK_SUCCESS; fail: @@ -248,6 +272,43 @@ hsk_rs_inject_options(hsk_rs_t *ns) { return true; } +static bool +hsk_rs_configure_fallback(hsk_rs_t *ns) { + if (ub_ctx_set_option(ns->fallback, "logfile:", "") != 0) + return false; + + if (ub_ctx_set_option(ns->fallback, "use-syslog:", "no") != 0) + return false; + + ub_ctx_set_option(ns->fallback, "trust-anchor-signaling:", "no"); + + if (ub_ctx_set_option(ns->fallback, "edns-buffer-size:", "4096") != 0) + return false; + + if (ub_ctx_set_option(ns->fallback, "max-udp-size:", "4096") != 0) + return false; + + ub_ctx_set_option(ns->fallback, "qname-minimisation:", "yes"); + + if (ub_ctx_set_option(ns->fallback, "root-hints:", "") != 0) + return false; + + if (ub_ctx_set_option(ns->fallback, "do-tcp:", "no") != 0) + return false; + + + if (ub_ctx_set_fwd(ns->fallback, ns->upstream) != 0) + return false; + + // Use a thread instead of forking for libunbound's async work. Threads work + // on all platforms, but forking does not work on Windows. + ub_ctx_async(ns->fallback, 1); + + hsk_rs_log(ns, "fallback resolver pointing to: %s\n", ns->upstream); + + return true; +} + int hsk_rs_open(hsk_rs_t *ns, const struct sockaddr *addr) { if (!ns || !addr) @@ -293,6 +354,19 @@ hsk_rs_open(hsk_rs_t *ns, const struct sockaddr *addr) { hsk_rs_log(ns, "recursive nameserver listening on: %s\n", host); + if (ns->upstream) { + if (!hsk_rs_configure_fallback(ns)) + return HSK_EFAILURE; + + ns->fallback_worker = hsk_rs_worker_alloc(ns->loop, (void *)ns, + after_worker_stop); + if (!ns->fallback_worker) + return HSK_EFAILURE; + + if (hsk_rs_worker_open(ns->fallback_worker, ns->fallback) != HSK_SUCCESS) + return HSK_EFAILURE; + } + return HSK_SUCCESS; } @@ -311,17 +385,24 @@ hsk_rs_close(hsk_rs_t *ns, void *stop_data, void (*stop_callback)(void *)) { else after_worker_stop((void *)ns); + if(ns->fallback_worker && hsk_rs_worker_is_open(ns->fallback_worker)) + hsk_rs_worker_close(ns->fallback_worker); + return HSK_SUCCESS; } hsk_rs_t * -hsk_rs_alloc(const uv_loop_t *loop, const struct sockaddr *stub) { +hsk_rs_alloc( + const uv_loop_t *loop, + const struct sockaddr *stub, + const char *upstream +) { hsk_rs_t *ns = malloc(sizeof(hsk_rs_t)); if (!ns) return NULL; - if (hsk_rs_init(ns, loop, stub) != HSK_SUCCESS) { + if (hsk_rs_init(ns, loop, stub, upstream) != HSK_SUCCESS) { free(ns); return NULL; } @@ -428,6 +509,12 @@ hsk_rs_respond( hsk_rs_log(ns, "received answer for: %s\n", req->name); + if (result->rcode == HSK_DNS_SERVFAIL) { + hsk_rs_log(ns, "received SERVFAIL for: %s\n", req->name); + + // TODO: Try same lookup again with fallback resolver + } + if (result->canonname) hsk_rs_log(ns, " canonname: %s\n", result->canonname); @@ -592,6 +679,11 @@ after_worker_stop(void *data) { ns->rs_worker = NULL; } + if (ns->fallback_worker) { + hsk_rs_worker_free(ns->fallback_worker); + ns->fallback_worker = NULL; + } + if (ns->receiving) { uv_udp_recv_stop(ns->socket); ns->receiving = false; diff --git a/src/rs.h b/src/rs.h index cb8e1bcf..0d1b2525 100644 --- a/src/rs.h +++ b/src/rs.h @@ -31,6 +31,9 @@ typedef struct { bool receiving; void *stop_data; void (*stop_callback)(void *); + char *upstream; + struct ub_ctx *fallback; + hsk_rs_worker_t *fallback_worker; } hsk_rs_t; /* @@ -38,7 +41,12 @@ typedef struct { */ int -hsk_rs_init(hsk_rs_t *ns, const uv_loop_t *loop, const struct sockaddr *stub); +hsk_rs_init( + hsk_rs_t *ns, + const uv_loop_t *loop, + const struct sockaddr *stub, + const char *upstream +); void hsk_rs_uninit(hsk_rs_t *ns); @@ -58,7 +66,11 @@ int hsk_rs_close(hsk_rs_t *ns, void *stop_data, void (*stop_callback)(void *)); hsk_rs_t * -hsk_rs_alloc(const uv_loop_t *loop, const struct sockaddr *stub); +hsk_rs_alloc( + const uv_loop_t *loop, + const struct sockaddr *stub, + const char *upstream +); void hsk_rs_free(hsk_rs_t *ns); From 2de44d4121c402aa42f377eeabedaedcc6033202 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 17 Mar 2021 15:43:10 -0400 Subject: [PATCH 3/5] rs: redirect query to fallback on SERVFAIL if --upstream is set --- src/rs.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/rs.c b/src/rs.c index 036c5fa4..0dc4c408 100644 --- a/src/rs.c +++ b/src/rs.c @@ -494,9 +494,9 @@ hsk_rs_onrecv( static void hsk_rs_respond( hsk_rs_t *ns, - const hsk_dns_req_t *req, + hsk_dns_req_t *req, int status, - const struct ub_result *result + struct ub_result *result ) { hsk_dns_msg_t *msg = NULL; uint8_t *wire = NULL; @@ -509,10 +509,25 @@ hsk_rs_respond( hsk_rs_log(ns, "received answer for: %s\n", req->name); - if (result->rcode == HSK_DNS_SERVFAIL) { - hsk_rs_log(ns, "received SERVFAIL for: %s\n", req->name); - - // TODO: Try same lookup again with fallback resolver + if (ns->upstream && result->rcode == HSK_DNS_SERVFAIL) { + hsk_rs_log(ns, "redirecting lookup to fallback for: %s\n", req->name); + + // Try same lookup again with fallback resolver + int rc; + rc = hsk_rs_worker_resolve( + ns->fallback_worker, + req->name, + req->type, + req->class, + (void *)req, + after_resolve + ); + + if (rc == HSK_SUCCESS) { + return; + } else { + goto fail; + } } if (result->canonname) @@ -586,6 +601,8 @@ hsk_rs_respond( done: hsk_rs_send(ns, wire, wire_len, req->addr, true); + ub_resolve_free(result); + hsk_dns_req_free(req); } static int @@ -771,11 +788,13 @@ after_resolve(void *data, int status, struct ub_result *result) { assert(ns); - // If the request is aborted, result is NULL, we just need to free req in that - // case + // If the request is aborted, result is NULL. + // We just need to free req in that case. + // hsk_rs_respond() will free both result and req + // when it is completely done (may recurse if using fallback). if (result) { hsk_rs_respond(ns, req, status, result); - ub_resolve_free(result); + } else { + hsk_dns_req_free(req); } - hsk_dns_req_free(req); } From 4b6458daca0f2c52c72e9621f91334cba2041668 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 17 Mar 2021 19:07:32 -0400 Subject: [PATCH 4/5] rs: clean up fallback_worker async stop and close --- src/rs.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/rs.c b/src/rs.c index 0dc4c408..2a3df738 100644 --- a/src/rs.c +++ b/src/rs.c @@ -55,6 +55,9 @@ alloc_buffer(uv_handle_t *handle, size_t size, uv_buf_t *buf); static void after_worker_stop(void *data); +static void +after_fallback_worker_stop(void *data); + static void after_send(uv_udp_send_t *req, int status); @@ -359,7 +362,7 @@ hsk_rs_open(hsk_rs_t *ns, const struct sockaddr *addr) { return HSK_EFAILURE; ns->fallback_worker = hsk_rs_worker_alloc(ns->loop, (void *)ns, - after_worker_stop); + after_fallback_worker_stop); if (!ns->fallback_worker) return HSK_EFAILURE; @@ -378,6 +381,11 @@ hsk_rs_close(hsk_rs_t *ns, void *stop_data, void (*stop_callback)(void *)) { ns->stop_data = stop_data; ns->stop_callback = stop_callback; + if(ns->fallback_worker && hsk_rs_worker_is_open(ns->fallback_worker)) + hsk_rs_worker_close(ns->fallback_worker); + else + after_fallback_worker_stop((void*)ns); + // If the worker is running, stop it, after_worker_stop is called // asynchronously. Otherwise, just call it directly. if(ns->rs_worker && hsk_rs_worker_is_open(ns->rs_worker)) @@ -385,9 +393,6 @@ hsk_rs_close(hsk_rs_t *ns, void *stop_data, void (*stop_callback)(void *)) { else after_worker_stop((void *)ns); - if(ns->fallback_worker && hsk_rs_worker_is_open(ns->fallback_worker)) - hsk_rs_worker_close(ns->fallback_worker); - return HSK_SUCCESS; } @@ -696,11 +701,6 @@ after_worker_stop(void *data) { ns->rs_worker = NULL; } - if (ns->fallback_worker) { - hsk_rs_worker_free(ns->fallback_worker); - ns->fallback_worker = NULL; - } - if (ns->receiving) { uv_udp_recv_stop(ns->socket); ns->receiving = false; @@ -725,6 +725,21 @@ after_worker_stop(void *data) { stop_callback(stop_data); } +static void +after_fallback_worker_stop(void *data) { + hsk_rs_t *ns = (hsk_rs_t *)data; + + if (ns->fallback_worker) { + hsk_rs_worker_free(ns->fallback_worker); + ns->fallback_worker = NULL; + } + + if (ns->fallback) { + ub_ctx_delete(ns->fallback); + ns->fallback = NULL; + } +} + static void after_send(uv_udp_send_t *req, int status) { hsk_send_data_t *sd = (hsk_send_data_t *)req->data; From 535a2bf89e435cfaee7a3c5244c7a05c955b3447 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Thu, 18 Mar 2021 15:09:35 -0400 Subject: [PATCH 5/5] [draft]: move fallback resolver to root ns --- src/daemon.c | 2 +- src/ns.c | 38 ++++++++++++++++++++++---------------- src/ns.h | 4 +++- src/rs.c | 35 +++++++---------------------------- src/rs.h | 3 +++ 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/src/daemon.c b/src/daemon.c index 51bda512..e621c2c8 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -513,7 +513,7 @@ hsk_daemon_open(hsk_daemon_t *daemon, hsk_options_t *opt) { return rc; } - rc = hsk_ns_open(daemon->ns, opt->ns_host); + rc = hsk_ns_open(daemon->ns, opt->ns_host, daemon->rs); if (rc != HSK_SUCCESS) { fprintf(stderr, "failed opening ns: %s\n", hsk_strerror(rc)); diff --git a/src/ns.c b/src/ns.c index 9fffbf1c..fb116e27 100644 --- a/src/ns.c +++ b/src/ns.c @@ -24,6 +24,8 @@ #include "utils.h" #include "uv.h" #include "dnssec.h" +#include "rs_worker.h" +#include "rs.h" // A RRSIG NSEC static const uint8_t hsk_type_map_a[] = { @@ -127,6 +129,7 @@ hsk_ns_init( memset(ns->read_buffer, 0x00, sizeof(ns->read_buffer)); ns->receiving = false; ns->upstream = upstream; + ns->rs = NULL; return HSK_SUCCESS; } @@ -186,7 +189,7 @@ hsk_ns_set_key(hsk_ns_t *ns, const uint8_t *key) { } int -hsk_ns_open(hsk_ns_t *ns, const struct sockaddr *addr) { +hsk_ns_open(hsk_ns_t *ns, const struct sockaddr *addr, hsk_rs_t *rs) { if (!ns || !addr) return HSK_EBADARGS; @@ -221,6 +224,8 @@ hsk_ns_open(hsk_ns_t *ns, const struct sockaddr *addr) { char host[HSK_MAX_HOST]; assert(hsk_sa_to_string(addr, host, HSK_MAX_HOST, HSK_NS_PORT)); + ns->rs = rs; + hsk_ns_log(ns, "root nameserver listening on: %s\n", host); return HSK_SUCCESS; @@ -521,16 +526,6 @@ hsk_ns_respond( if (status != HSK_SUCCESS) { // Pool resolve error. hsk_ns_log(ns, "resolve response error: %s\n", hsk_strerror(status)); - - // Forward to custom upstream resolver - if (status == HSK_EREFUSED) { - msg = hsk_resource_to_refused(); - - if (!msg) - hsk_ns_log(ns, "could not create refused response (%u)\n", req->id); - else - hsk_ns_log(ns, "sending refused (%u)\n", req->id); - } } else if (!res) { // Doesn't exist. // @@ -749,11 +744,22 @@ after_resolve( if (ns->upstream) { // User has requested that all non-HNS queries // get forwarded to a specific upstream resolver. - // Instead of sending NXDOMAIN we respond to the recursive - // with a REFUSED error as a special cue. - hsk_ns_log(ns, "forwarding to upstream resolver: %s\n", name); - status = HSK_EREFUSED; - res = NULL; + hsk_ns_log(ns, "forwarding to upstream resolver: %s\n", req->name); + + int rc; + rc = hsk_rs_worker_resolve( + ns->rs->fallback_worker, + req->name, + req->type, + req->class, + (void *)req, + rs_after_resolve + ); + + if (res) + hsk_resource_free(res); + + return; } else { const uint8_t *item = hsk_icann_lookup(name); diff --git a/src/ns.h b/src/ns.h index 9dfc3c07..6d3c09f8 100644 --- a/src/ns.h +++ b/src/ns.h @@ -9,6 +9,7 @@ #include "cache.h" #include "ec.h" #include "pool.h" +#include "rs.h" /* * Defs @@ -34,6 +35,7 @@ typedef struct { uint8_t read_buffer[HSK_UDP_BUFFER]; bool receiving; bool upstream; + hsk_rs_t *rs; } hsk_ns_t; /* @@ -58,7 +60,7 @@ bool hsk_ns_set_key(hsk_ns_t *ns, const uint8_t *key); int -hsk_ns_open(hsk_ns_t *ns, const struct sockaddr *addr); +hsk_ns_open(hsk_ns_t *ns, const struct sockaddr *addr, hsk_rs_t *rs); int hsk_ns_close(hsk_ns_t *ns); diff --git a/src/rs.c b/src/rs.c index 2a3df738..db1f216e 100644 --- a/src/rs.c +++ b/src/rs.c @@ -70,9 +70,6 @@ after_recv( unsigned flags ); -static void -after_resolve(void *data, int status, struct ub_result *result); - /* * Recursive NS */ @@ -242,7 +239,10 @@ hsk_rs_inject_options(hsk_rs_t *ns) { if (ub_ctx_set_option(ns->ub, "max-udp-size:", "4096") != 0) return false; - ub_ctx_set_option(ns->ub, "qname-minimisation:", "yes"); + if (ns->upstream) + ub_ctx_set_option(ns->ub, "qname-minimisation:", "no"); + else + ub_ctx_set_option(ns->ub, "qname-minimisation:", "yes"); if (ub_ctx_set_option(ns->ub, "root-hints:", "") != 0) return false; @@ -469,7 +469,7 @@ hsk_rs_onrecv( req->type, req->class, (void *)req, - after_resolve + rs_after_resolve ); if (rc == HSK_SUCCESS) @@ -514,27 +514,6 @@ hsk_rs_respond( hsk_rs_log(ns, "received answer for: %s\n", req->name); - if (ns->upstream && result->rcode == HSK_DNS_SERVFAIL) { - hsk_rs_log(ns, "redirecting lookup to fallback for: %s\n", req->name); - - // Try same lookup again with fallback resolver - int rc; - rc = hsk_rs_worker_resolve( - ns->fallback_worker, - req->name, - req->type, - req->class, - (void *)req, - after_resolve - ); - - if (rc == HSK_SUCCESS) { - return; - } else { - goto fail; - } - } - if (result->canonname) hsk_rs_log(ns, " canonname: %s\n", result->canonname); @@ -796,8 +775,8 @@ after_recv( ); } -static void -after_resolve(void *data, int status, struct ub_result *result) { +void +rs_after_resolve(void *data, int status, struct ub_result *result) { hsk_dns_req_t *req = (hsk_dns_req_t *)data; hsk_rs_t *ns = (hsk_rs_t *)req->ns; diff --git a/src/rs.h b/src/rs.h index 0d1b2525..34775086 100644 --- a/src/rs.h +++ b/src/rs.h @@ -72,6 +72,9 @@ hsk_rs_alloc( const char *upstream ); +void +rs_after_resolve(void *data, int status, struct ub_result *result); + void hsk_rs_free(hsk_rs_t *ns); #endif