From 6ea05ac3fe9264ae12d8c62398d22a375995bee1 Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Thu, 8 Dec 2022 14:18:22 +0000 Subject: [PATCH] Resolver query forwarding to DoT-enabled upstream servers Implement TLS transport usage in the resolver. Use the configured TLS transport for the forwarders in the resolver. --- bin/delv/delv.c | 13 +++++++++-- bin/named/server.c | 3 ++- doc/arm/reference.rst | 8 +++++-- lib/dns/adb.c | 4 ++++ lib/dns/client.c | 15 ++++++++----- lib/dns/include/dns/adb.h | 5 +++-- lib/dns/include/dns/client.h | 3 ++- lib/dns/include/dns/resolver.h | 11 ++++++--- lib/dns/include/dns/view.h | 6 +++-- lib/dns/resolver.c | 41 +++++++++++++++++++++++++++++----- lib/dns/view.c | 5 +++-- tests/dns/resolver_test.c | 9 +++++++- 12 files changed, 96 insertions(+), 27 deletions(-) diff --git a/bin/delv/delv.c b/bin/delv/delv.c index 84e808894a..132d5dda34 100644 --- a/bin/delv/delv.c +++ b/bin/delv/delv.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,9 @@ static isc_nm_t *netmgr = NULL; static isc_loopmgr_t *loopmgr = NULL; static isc_taskmgr_t *taskmgr = NULL; +/* TLS */ +static isc_tlsctx_cache_t *tlsctx_client_cache = NULL; + /* Configurables */ static char *server = NULL; static const char *port = "53"; @@ -1835,8 +1839,10 @@ main(int argc, char *argv[]) { setup_logging(stderr); /* Create client */ - result = dns_client_create(mctx, loopmgr, taskmgr, netmgr, 0, &client, - srcaddr4, srcaddr6); + isc_tlsctx_cache_create(mctx, &tlsctx_client_cache); + result = dns_client_create(mctx, loopmgr, taskmgr, netmgr, 0, + tlsctx_client_cache, &client, srcaddr4, + srcaddr6); if (result != ISC_R_SUCCESS) { delv_log(ISC_LOG_ERROR, "dns_client_create: %s", isc_result_totext(result)); @@ -1869,6 +1875,9 @@ cleanup: if (style != NULL) { dns_master_styledestroy(&style, mctx); } + if (tlsctx_client_cache != NULL) { + isc_tlsctx_cache_detach(&tlsctx_client_cache); + } isc_log_destroy(&lctx); diff --git a/bin/named/server.c b/bin/named/server.c index cf9fe130e9..9efce19bad 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -4758,7 +4758,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, ndisp = 4 * ISC_MIN(named_g_udpdisp, MAX_UDP_DISPATCH); CHECK(dns_view_createresolver( view, named_g_loopmgr, named_g_taskmgr, ndisp, named_g_netmgr, - resopts, named_g_dispatchmgr, dispatch4, dispatch6)); + resopts, named_g_server->tlsctx_client_cache, + named_g_dispatchmgr, dispatch4, dispatch6)); if (resstats == NULL) { CHECK(isc_stats_create(mctx, &resstats, diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 23e55d2f76..bf93f50272 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2884,8 +2884,12 @@ authoritative and does not have the answer in its cache. This specifies a list of IP addresses to which queries are forwarded. The default is the empty list (no forwarding). Each address in the list can be - associated with an optional port number. A default port number can be set - for the entire list. + associated with an optional port number and a TLS transport. A default port + number and a TLS transport can be set for the entire list. + + If a TLS configuration is specified, :iscman:`named` will use DNS-over-TLS + (DoT) connections when connecting to the specified IP address(es), using the + TLS configuration referenced by the :any:`tls` statement. Forwarding can also be configured on a per-domain basis, allowing for the global forwarding options to be overridden in a variety of ways. diff --git a/lib/dns/adb.c b/lib/dns/adb.c index d5a17497fd..7fff6ead7d 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -49,6 +49,7 @@ #include #include #include +#include #define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b') #define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC) @@ -1321,6 +1322,9 @@ free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) { ai->magic = 0; + if (ai->transport != NULL) { + dns_transport_detach(&ai->transport); + } dns_adbentry_detach(&ai->entry); isc_mem_put(adb->mctx, ai, sizeof(*ai)); diff --git a/lib/dns/client.c b/lib/dns/client.c index f160f7650a..5e76b684a1 100644 --- a/lib/dns/client.c +++ b/lib/dns/client.c @@ -204,9 +204,10 @@ getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr, static isc_result_t createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_loopmgr_t *loopmgr, - isc_taskmgr_t *taskmgr, isc_nm_t *nm, dns_dispatchmgr_t *dispatchmgr, - dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, - dns_view_t **viewp) { + isc_taskmgr_t *taskmgr, isc_nm_t *nm, + isc_tlsctx_cache_t *tlsctx_client_cache, + dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, + dns_dispatch_t *dispatchv6, dns_view_t **viewp) { isc_result_t result; dns_view_t *view = NULL; @@ -222,7 +223,8 @@ createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_loopmgr_t *loopmgr, } result = dns_view_createresolver(view, loopmgr, taskmgr, 1, nm, 0, - dispatchmgr, dispatchv4, dispatchv6); + tlsctx_client_cache, dispatchmgr, + dispatchv4, dispatchv6); if (result != ISC_R_SUCCESS) { goto cleanup_view; } @@ -244,6 +246,7 @@ cleanup_view: isc_result_t dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, isc_nm_t *nm, unsigned int options, + isc_tlsctx_cache_t *tlsctx_client_cache, dns_client_t **clientp, const isc_sockaddr_t *localaddr4, const isc_sockaddr_t *localaddr6) { isc_result_t result; @@ -255,6 +258,7 @@ dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, REQUIRE(mctx != NULL); REQUIRE(taskmgr != NULL); REQUIRE(nm != NULL); + REQUIRE(tlsctx_client_cache != NULL); REQUIRE(clientp != NULL && *clientp == NULL); UNUSED(options); @@ -309,7 +313,8 @@ dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, /* Create the default view for class IN */ result = createview(mctx, dns_rdataclass_in, loopmgr, taskmgr, nm, - client->dispatchmgr, dispatchv4, dispatchv6, &view); + tlsctx_client_cache, client->dispatchmgr, + dispatchv4, dispatchv6, &view); if (result != ISC_R_SUCCESS) { goto cleanup_references; } diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index e5cdf4711e..fc50d8f994 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -204,8 +204,9 @@ struct dns_adbfind { struct dns_adbaddrinfo { unsigned int magic; /*%< private */ - isc_sockaddr_t sockaddr; /*%< [rw] */ - unsigned int srtt; /*%< [rw] microsecs */ + isc_sockaddr_t sockaddr; /*%< [rw] */ + unsigned int srtt; /*%< [rw] microsecs */ + dns_transport_t *transport; unsigned int flags; /*%< [rw] */ dns_adbentry_t *entry; /*%< private */ diff --git a/lib/dns/include/dns/client.h b/lib/dns/include/dns/client.h index 0caf154e61..874a609936 100644 --- a/lib/dns/include/dns/client.h +++ b/lib/dns/include/dns/client.h @@ -91,6 +91,7 @@ typedef struct dns_clientresevent { isc_result_t dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, isc_nm_t *nm, unsigned int options, + isc_tlsctx_cache_t *tlsctx_client_cache, dns_client_t **clientp, const isc_sockaddr_t *localaddr4, const isc_sockaddr_t *localaddr6); /*%< @@ -113,7 +114,7 @@ dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, * *\li 'nm' is a valid network manager. * - *\li 'timermgr' is a valid timer manager. + *\li 'tlsctx_client_cache' is a valid TLS context cache. * *\li clientp != NULL && *clientp == NULL. * diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index ff8397dc94..cfae9f438e 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -171,9 +172,9 @@ typedef enum { dns_quotatype_zone = 0, dns_quotatype_server } dns_quotatype_t; isc_result_t dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, unsigned int ndisp, isc_nm_t *nm, - unsigned int options, dns_dispatchmgr_t *dispatchmgr, - dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, - dns_resolver_t **resp); + unsigned int options, isc_tlsctx_cache_t *tlsctx_cache, + dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, + dns_dispatch_t *dispatchv6, dns_resolver_t **resp); /*%< * Create a resolver. @@ -193,6 +194,10 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, * *\li 'nm' is a valid network manager. * + *\li 'tlsctx_cache' != NULL. + * + *\li 'dispatchmgr' != NULL. + * *\li 'dispatchv4' is a dispatch with an IPv4 UDP socket, or is NULL. * If not NULL, 'ndisp' clones of it will be created by the resolver. * diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index e4dd0d178f..464114ea67 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -390,7 +390,8 @@ isc_result_t dns_view_createresolver(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, unsigned int ndisp, isc_nm_t *netmgr, unsigned int options, - dns_dispatchmgr_t *dispatchmgr, + isc_tlsctx_cache_t *tlsctx_cache, + dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6); /*%< * Create a resolver and address database for the view. @@ -402,7 +403,8 @@ dns_view_createresolver(dns_view_t *view, isc_loopmgr_t *loopmgr, *\li 'view' does not have a resolver already. * *\li The requirements of dns_resolver_create() apply to 'taskmgr', - * 'ndisp', 'netmgr', 'options', 'dispatchv4', and 'dispatchv6'. + * 'ndisp', 'netmgr', 'options', 'tlsctx_cache', 'dispatchv4', and + * 'dispatchv6'. * * Returns: * diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 973337f2ac..d663e2bc89 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -548,6 +548,7 @@ struct dns_resolver { dns_view_t *view; bool frozen; unsigned int options; + isc_tlsctx_cache_t *tlsctx_cache; dns_dispatchmgr_t *dispatchmgr; dns_dispatchset_t *dispatches4; dns_dispatchset_t *dispatches6; @@ -2134,6 +2135,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_sockaddr_t addr; bool have_addr = false; unsigned int srtt; + isc_tlsctx_cache_t *tlsctx_cache = NULL; FCTXTRACE("query"); @@ -2141,6 +2143,21 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, srtt = addrinfo->srtt; + if (addrinfo->transport != NULL) { + switch (dns_transport_get_type(addrinfo->transport)) { + case DNS_TRANSPORT_TLS: + options |= DNS_FETCHOPT_TCP; + tlsctx_cache = res->tlsctx_cache; + break; + case DNS_TRANSPORT_TCP: + case DNS_TRANSPORT_HTTP: + options |= DNS_FETCHOPT_TCP; + break; + default: + break; + } + } + /* * Allow an additional second for the kernel to resend the SYN * (or SYN without ECN in the case of stupid firewalls blocking @@ -2301,9 +2318,9 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, /* Set up the dispatch and set the query ID */ result = dns_dispatch_add( query->dispatch, 0, isc_interval_ms(&fctx->interval), - &query->addrinfo->sockaddr, NULL, NULL, resquery_connected, - resquery_senddone, resquery_response, query, &query->id, - &query->dispentry); + &query->addrinfo->sockaddr, addrinfo->transport, tlsctx_cache, + resquery_connected, resquery_senddone, resquery_response, query, + &query->id, &query->dispentry); if (result != ISC_R_SUCCESS) { goto cleanup_udpfetch; } @@ -3679,6 +3696,15 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) { if (result == ISC_R_SUCCESS) { dns_adbaddrinfo_t *cur; ai->flags |= FCTX_ADDRINFO_FORWARDER; + if (fwd->tlsname != NULL) { + result = dns_view_gettransport( + res->view, DNS_TRANSPORT_TLS, + fwd->tlsname, &ai->transport); + if (result != ISC_R_SUCCESS) { + dns_adb_freeaddrinfo(fctx->adb, &ai); + goto next; + } + } cur = ISC_LIST_HEAD(fctx->forwaddrs); while (cur != NULL && cur->srtt < ai->srtt) { cur = ISC_LIST_NEXT(cur, publink); @@ -3690,6 +3716,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) { ISC_LIST_APPEND(fctx->forwaddrs, ai, publink); } } + next: fwd = ISC_LIST_NEXT(fwd, link); } @@ -10095,9 +10122,9 @@ spillattimer_countdown(void *arg) { isc_result_t dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, unsigned int ndisp, isc_nm_t *nm, - unsigned int options, dns_dispatchmgr_t *dispatchmgr, - dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6, - dns_resolver_t **resp) { + unsigned int options, isc_tlsctx_cache_t *tlsctx_cache, + dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, + dns_dispatch_t *dispatchv6, dns_resolver_t **resp) { isc_result_t result = ISC_R_SUCCESS; char name[sizeof("res4294967295")]; dns_resolver_t *res = NULL; @@ -10109,6 +10136,7 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(ndisp > 0); REQUIRE(resp != NULL && *resp == NULL); + REQUIRE(tlsctx_cache != NULL); REQUIRE(dispatchmgr != NULL); REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL); @@ -10122,6 +10150,7 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr, .taskmgr = taskmgr, .dispatchmgr = dispatchmgr, .options = options, + .tlsctx_cache = tlsctx_cache, .spillatmin = 10, .spillat = 10, .spillatmax = 100, diff --git a/lib/dns/view.c b/lib/dns/view.c index bd0af302bd..e8a53aa108 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -655,6 +655,7 @@ isc_result_t dns_view_createresolver(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_taskmgr_t *taskmgr, unsigned int ndisp, isc_nm_t *netmgr, unsigned int options, + isc_tlsctx_cache_t *tlsctx_cache, dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6) { @@ -672,8 +673,8 @@ dns_view_createresolver(dns_view_t *view, isc_loopmgr_t *loopmgr, isc_task_setname(view->task, "view", view); result = dns_resolver_create(view, loopmgr, taskmgr, ndisp, netmgr, - options, dispatchmgr, dispatchv4, - dispatchv6, &view->resolver); + options, tlsctx_cache, dispatchmgr, + dispatchv4, dispatchv6, &view->resolver); if (result != ISC_R_SUCCESS) { isc_task_detach(&view->task); return (result); diff --git a/tests/dns/resolver_test.c b/tests/dns/resolver_test.c index 33914b4d70..51b6b35faa 100644 --- a/tests/dns/resolver_test.c +++ b/tests/dns/resolver_test.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ static dns_dispatchmgr_t *dispatchmgr = NULL; static dns_dispatch_t *dispatch = NULL; static dns_view_t *view = NULL; +static isc_tlsctx_cache_t *tlsctx_cache = NULL; static int setup_test(void **state) { @@ -73,8 +75,10 @@ static void mkres(dns_resolver_t **resolverp) { isc_result_t result; + isc_tlsctx_cache_create(mctx, &tlsctx_cache); result = dns_resolver_create(view, loopmgr, taskmgr, 1, netmgr, 0, - dispatchmgr, dispatch, NULL, resolverp); + tlsctx_cache, dispatchmgr, dispatch, NULL, + resolverp); assert_int_equal(result, ISC_R_SUCCESS); } @@ -82,6 +86,9 @@ static void destroy_resolver(dns_resolver_t **resolverp) { dns_resolver_shutdown(*resolverp); dns_resolver_detach(resolverp); + if (tlsctx_cache != NULL) { + isc_tlsctx_cache_detach(&tlsctx_cache); + } } /* dns_resolver_create */