]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
prevent a shutdown hang on non-matching TCP responses
authorEvan Hunt <each@isc.org>
Thu, 2 Dec 2021 20:04:42 +0000 (12:04 -0800)
committerEvan Hunt <each@isc.org>
Wed, 8 Dec 2021 18:22:03 +0000 (10:22 -0800)
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

index d5cbd07dcfdffdcd1c8f6c9bbb7cbb2436afe96c..ac0afb51dde3068eba80e91a9852a9c786de2dd0 100644 (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: