From 2be0acf3f37a35d7dfad67f060f343402664136c Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Tue, 9 Jan 2024 19:16:26 +0200 Subject: [PATCH] Recreate listeners on DNS transport change This commit ensures that listeners are recreated on reconfiguration in the case when their type changes (or when PROXY protocol type changes, too). Previously, if a "listen-on" statement was modified to represent a different transport, BIND would not pick-up the change on reconfiguration if listener type changes (e.g. DoH -> DoT) for a given interface address and port combination. This commit fixes that by recreating the listener. Initially, that worked for most of the new transports as we would recreate listeners on each reconfiguration for DoH and DoT. But at some point we changed that in such a way that listeners were not recreated to avoid rebinding a port as on some platforms only root can do that for port numbers <1000, making some ports binding possible only on start-up. We chose to asynchronously update listener socket settings (like TLS contexts, HTTP settings) instead. Now, we both avoid recreating the sockets if unnecessary and recreate listeners when listener type changes. (cherry picked from commit d59cf5e0cec7aba9048f685e3cfd1f0e495f474c) --- lib/ns/include/ns/interfacemgr.h | 1 + lib/ns/interfacemgr.c | 96 +++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/lib/ns/include/ns/interfacemgr.h b/lib/ns/include/ns/interfacemgr.h index 028f86d570..0ae6fffd39 100644 --- a/lib/ns/include/ns/interfacemgr.h +++ b/lib/ns/include/ns/interfacemgr.h @@ -77,6 +77,7 @@ struct ns_interface { char name[32]; /*%< Null terminated. */ isc_nmsocket_t *udplistensocket; isc_nmsocket_t *tcplistensocket; + isc_nmsocket_t *tlslistensocket; isc_nmsocket_t *http_listensocket; isc_nmsocket_t *http_secure_listensocket; isc_quota_t *http_quota; diff --git a/lib/ns/interfacemgr.c b/lib/ns/interfacemgr.c index 50b87dfb5e..d9f479046f 100644 --- a/lib/ns/interfacemgr.c +++ b/lib/ns/interfacemgr.c @@ -541,7 +541,7 @@ ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) { result = isc_nm_listentlsdns( ifp->mgr->nm, &ifp->addr, ns__client_request, ifp, ns__client_tcpconn, ifp, sizeof(ns_client_t), ifp->mgr->backlog, - &ifp->mgr->sctx->tcpquota, sslctx, &ifp->tcplistensocket); + &ifp->mgr->sctx->tcpquota, sslctx, &ifp->tlslistensocket); if (result != ISC_R_SUCCESS) { isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR, @@ -672,6 +672,9 @@ interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr, const char *name, interface_create(mgr, addr, name, &ifp); } else { REQUIRE(!LISTENING(ifp)); + LOCK(&mgr->lock); + ifp->generation = mgr->generation; + UNLOCK(&mgr->lock); } ifp->flags |= NS_INTERFACEFLAG_LISTENING; @@ -743,6 +746,10 @@ ns_interface_shutdown(ns_interface_t *ifp) { isc_nm_stoplistening(ifp->tcplistensocket); isc_nmsocket_close(&ifp->tcplistensocket); } + if (ifp->tlslistensocket != NULL) { + isc_nm_stoplistening(ifp->tlslistensocket); + isc_nmsocket_close(&ifp->tlslistensocket); + } if (ifp->http_listensocket != NULL) { isc_nm_stoplistening(ifp->http_listensocket); isc_nmsocket_close(&ifp->http_listensocket); @@ -798,6 +805,14 @@ find_matching_interface(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) { return (ifp); } +static void +log_interface_shutdown(const ns_interface_t *ifp) { + char sabuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf)); + isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, + "no longer listening on %s", sabuf); +} + /*% * Remove any interfaces whose generation number is not the current one. */ @@ -822,10 +837,7 @@ purge_old_interfaces(ns_interfacemgr_t *mgr) { for (ifp = ISC_LIST_HEAD(interfaces); ifp != NULL; ifp = next) { next = ISC_LIST_NEXT(ifp, link); if (LISTENING(ifp)) { - char sabuf[256]; - isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf)); - isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, - "no longer listening on %s", sabuf); + log_interface_shutdown(ifp); ns_interface_shutdown(ifp); } ISC_LIST_UNLINK(interfaces, ifp, link); @@ -942,9 +954,8 @@ replace_listener_tlsctx(ns_interface_t *ifp, isc_tlsctx_t *newctx) { isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf)); isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_INFO, "updating TLS context on %s", sabuf); - if (ifp->tcplistensocket != NULL) { - /* 'tcplistensocket' is used for DoT */ - isc_nmsocket_set_tlsctx(ifp->tcplistensocket, newctx); + if (ifp->tlslistensocket != NULL) { + isc_nmsocket_set_tlsctx(ifp->tlslistensocket, newctx); } else if (ifp->http_secure_listensocket != NULL) { isc_nmsocket_set_tlsctx(ifp->http_secure_listensocket, newctx); } @@ -1015,6 +1026,57 @@ update_listener_configuration(ns_interfacemgr_t *mgr, ns_interface_t *ifp, UNLOCK(&mgr->lock); } +static bool +same_listener_type(ns_interface_t *ifp, ns_listenelt_t *new_le) { + bool same_transport_type = false; + + if (new_le->is_http && new_le->sslctx != NULL && + ifp->http_secure_listensocket != NULL) + { + /* HTTPS/DoH */ + same_transport_type = true; + } else if (new_le->is_http && new_le->sslctx == NULL && + ifp->http_listensocket != NULL) + { + /* HTTP/plain DoH */ + same_transport_type = true; + } else if (new_le->sslctx != NULL && ifp->tlslistensocket != NULL) { + /* TLS/DoT */ + same_transport_type = true; + } else if (new_le->sslctx == NULL && (ifp->udplistensocket != NULL || + ifp->tcplistensocket != NULL)) + { + /* "plain" DNS/Do53 */ + same_transport_type = true; + } + + return (same_transport_type); +} + +static bool +interface_update_or_shutdown(ns_interfacemgr_t *mgr, ns_interface_t *ifp, + ns_listenelt_t *le, const bool config) { + if (LISTENING(ifp) && config && !same_listener_type(ifp, le)) { + /* + * DNS listener type has been changed on re-configuration. We + * will need to recreate the listener anew. + */ + log_interface_shutdown(ifp); + ns_interface_shutdown(ifp); + } else { + LOCK(&mgr->lock); + ifp->generation = mgr->generation; + UNLOCK(&mgr->lock); + if (LISTENING(ifp)) { + if (config) { + update_listener_configuration(mgr, ifp, le); + } + return (true); + } + } + return (false); +} + static isc_result_t do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { isc_interfaceiter_t *iter = NULL; @@ -1082,12 +1144,9 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { ifp = find_matching_interface(mgr, &listen_addr); if (ifp != NULL) { - ifp->generation = mgr->generation; - if (LISTENING(ifp)) { - if (config) { - update_listener_configuration( - mgr, ifp, le); - } + bool cont = interface_update_or_shutdown( + mgr, ifp, le, config); + if (cont) { continue; } } @@ -1233,12 +1292,9 @@ do_scan(ns_interfacemgr_t *mgr, bool verbose, bool config) { ifp = find_matching_interface(mgr, &listen_sockaddr); if (ifp != NULL) { - ifp->generation = mgr->generation; - if (LISTENING(ifp)) { - if (config) { - update_listener_configuration( - mgr, ifp, le); - } + bool cont = interface_update_or_shutdown( + mgr, ifp, le, config); + if (cont) { continue; } }