From 5f82fc11a9cf2f847540df256584933f6402bfa2 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 2 Dec 2021 12:04:42 -0800 Subject: [PATCH] prevent a shutdown hang on non-matching TCP responses When a non-matching DNS response is received by the resolver, it calls dns_dispatch_getnext() to resume reading. This is necessary for UDP but not for TCP, because TCP connections automatically resume reading after any valid DNS response. This commit adds a 'tcpreading' flag to TCP dispatches, so that `dispatch_getnext()` can be called multiple times without subsequent calls having any effect. --- lib/dns/dispatch.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index d5cbd07dcf..ac0afb51dd 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -125,6 +125,7 @@ struct dns_dispatch { isc_mutex_t lock; /*%< locks all below */ isc_socktype_t socktype; atomic_uint_fast32_t state; + atomic_bool tcpreading; isc_refcount_t references; unsigned int shutdown_out : 1; @@ -752,6 +753,8 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region, REQUIRE(VALID_DISPATCH(disp)); + atomic_store(&disp->tcpreading, false); + qid = disp->mgr->qid; ISC_LIST_INIT(resps); @@ -1531,11 +1534,14 @@ dispatch_getnext(dns_dispatch_t *disp, dns_dispentry_t *resp, int32_t timeout) { break; case isc_socktype_tcp: - dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL }); - if (timeout > 0) { - isc_nmhandle_settimeout(disp->handle, timeout); + if (atomic_compare_exchange_strong(&disp->tcpreading, + &(bool){ false }, true)) { + dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL }); + if (timeout > 0) { + isc_nmhandle_settimeout(disp->handle, timeout); + } + isc_nm_read(disp->handle, tcp_recv, disp); } - isc_nm_read(disp->handle, tcp_recv, disp); break; default: