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.
This commit is contained in:
Evan Hunt
2021-12-02 12:04:42 -08:00
parent 0059433106
commit 5f82fc11a9

View File

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