]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Ignore TCP dispatches that have zero references
authorOndřej Surý <ondrej@isc.org>
Tue, 20 Dec 2022 05:11:26 +0000 (06:11 +0100)
committerOndřej Surý <ondrej@isc.org>
Wed, 21 Dec 2022 12:41:15 +0000 (12:41 +0000)
The TCP dispatches are removed from the dispatchmgr->list in the
dispatch_destroy() and there's a brief period of time where
dns_dispatch_gettcp() can find a dispatch in connected state that's
being destroyed.

Set the dispatch state to DNS_DISPATCHSTATE_NONE in the TCP connection
callback if there are no responses waiting, and ignore TCP dispatches
with zero references in dns_dispatch_gettcp().

(cherry picked from commit 3fac4ca57ee5d38b220a77e8089ac7e4bdb8cef2)

lib/dns/dispatch.c

index 3fbdb7f1489951623cd9561bbd992dc0146b3956..af796eebd381a3cff982a29fc47a4cda511693ab 100644 (file)
@@ -738,6 +738,7 @@ tcp_recv_shutdown(dns_dispatch_t *disp, dns_displist_t *resps,
                next = ISC_LIST_NEXT(resp, alink);
                tcp_recv_add(resps, resp, result);
        }
+       disp->state = DNS_DISPATCHSTATE_CANCELED;
 }
 
 static void
@@ -1232,34 +1233,42 @@ dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
                 * 2. destination address is same
                 * 3. local address is either NULL or same
                 */
-               if (disp->socktype == isc_socktype_tcp &&
-                   isc_sockaddr_equal(destaddr, &peeraddr) &&
-                   (localaddr == NULL ||
-                    isc_sockaddr_eqaddr(localaddr, &sockname)))
+               if (disp->socktype != isc_socktype_tcp ||
+                   !isc_sockaddr_equal(destaddr, &peeraddr) ||
+                   (localaddr != NULL &&
+                    !isc_sockaddr_eqaddr(localaddr, &sockname)))
                {
-                       switch (disp->state) {
-                       case DNS_DISPATCHSTATE_NONE:
-                               /* Dispatch in indeterminate state, skip it */
-                               break;
-                       case DNS_DISPATCHSTATE_CONNECTED:
-                               /* We found a connected dispatch */
-                               dns_dispatch_attach(disp, &disp_connected);
-                               break;
-                       case DNS_DISPATCHSTATE_CONNECTING:
-                               /* We found "a" dispatch, store it for later */
-                               if (disp_fallback == NULL) {
-                                       dns_dispatch_attach(disp,
-                                                           &disp_fallback);
-                               }
+                       UNLOCK(&disp->lock);
+                       continue;
+               }
+
+               switch (disp->state) {
+               case DNS_DISPATCHSTATE_NONE:
+                       /* A dispatch in indeterminate state, skip it */
+                       break;
+               case DNS_DISPATCHSTATE_CONNECTED:
+                       if (ISC_LIST_EMPTY(disp->active)) {
+                               /* Ignore dispatch with no responses */
                                break;
-                       case DNS_DISPATCHSTATE_CANCELED:
-                               /*
-                                * We found a canceled dispatch, skip it.
-                                */
+                       }
+                       /* We found a connected dispatch */
+                       dns_dispatch_attach(disp, &disp_connected);
+                       break;
+               case DNS_DISPATCHSTATE_CONNECTING:
+                       if (ISC_LIST_EMPTY(disp->pending)) {
+                               /* Ignore dispatch with no responses */
                                break;
-                       default:
-                               UNREACHABLE();
                        }
+                       /* We found "a" dispatch, store it for later */
+                       if (disp_fallback == NULL) {
+                               dns_dispatch_attach(disp, &disp_fallback);
+                       }
+                       break;
+               case DNS_DISPATCHSTATE_CANCELED:
+                       /* A canceled dispatch, skip it. */
+                       break;
+               default:
+                       UNREACHABLE();
                }
 
                UNLOCK(&disp->lock);
@@ -1844,7 +1853,10 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        LOCK(&disp->lock);
        INSIST(disp->state == DNS_DISPATCHSTATE_CONNECTING);
 
-       if (eresult == ISC_R_SUCCESS) {
+       if (ISC_LIST_EMPTY(disp->pending)) {
+               /* All responses have been canceled */
+               disp->state = DNS_DISPATCHSTATE_CANCELED;
+       } else if (eresult == ISC_R_SUCCESS) {
                disp->state = DNS_DISPATCHSTATE_CONNECTED;
                tcp_startrecv(handle, disp, resp);
        } else {