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 d59cf5e0ce)
This commit is contained in:
Artem Boldariev 2024-01-09 19:16:26 +02:00
parent d12ff65491
commit 2be0acf3f3
2 changed files with 77 additions and 20 deletions

View file

@ -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;

View file

@ -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;
}
}