bind/bind-9.18-fix-dig-hanging-issue.patch
Fedor Vorobev 46be8c89ec Backport fix of dig/nslookup hanging issue
Resolves: RHEL-29712
2026-02-26 12:30:48 +01:00

167 lines
5.0 KiB
Diff

diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 7e88b6b63c..768c4e56da 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -1577,6 +1577,31 @@ check_if_done(void) {
}
}
+
+/*%
+ * Check if we're done with all the queries in the lookup, except for
+ * the `except_q` query (can be NULL if no exception is required).
+ * Expects `l` to be a valid and locked lookup.
+ */
+static bool
+check_if_queries_done(dig_lookup_t *l, dig_query_t *except_q) {
+ dig_query_t *q = ISC_LIST_HEAD(l->q);
+
+ debug("check_if_queries_done(%p)", l);
+
+ while (q != NULL) {
+ if (!q->started || q->waiting_connect || q->waiting_senddone || q->pending_free) {
+ if (!q->canceled && q != except_q) {
+ debug("there is a pending query %p", q);
+ return (false);
+ }
+ }
+ q = ISC_LIST_NEXT(q, link);
+ }
+
+ return (true);
+}
+
/*%
* Clear out a query when we're done with it. WARNING: This routine
* WILL invalidate the query pointer.
@@ -2563,7 +2588,6 @@ setup_lookup(dig_lookup_t *lookup) {
query->waiting_senddone = false;
query->pending_free = false;
query->recv_made = false;
- query->first_pass = true;
query->first_soa_rcvd = false;
query->second_rr_rcvd = false;
query->first_repeat_rcvd = false;
@@ -2663,6 +2687,7 @@ cancel_lookup(dig_lookup_t *lookup) {
while (query != NULL) {
REQUIRE(DIG_VALID_QUERY(query));
next = ISC_LIST_NEXT(query, link);
+ query->canceled = true;
if (query->sock != NULL) {
isc_socket_cancel(query->sock, global_task,
ISC_SOCKCANCEL_ALL);
@@ -2905,6 +2930,12 @@ send_udp(dig_query_t *query) {
return;
}
+ query->started = true;
+ if (atomic_load(&cancel_now)) {
+ return;
+ }
+ INSIST(!free_now);
+
result = isc_socket_create(socketmgr,
isc_sockaddr_pf(&query->sockaddr),
isc_sockettype_udp, &query->sock);
@@ -3031,6 +3062,10 @@ connect_timeout(isc_task_t *task, isc_event_t *event) {
clear_query(query);
}
}
+ if (check_if_queries_done(l, query)) {
+ cancel_lookup(l);
+ check_next_lookup(l);
+ }
UNLOCK_LOOKUP;
return;
}
@@ -3286,6 +3321,14 @@ connect_done(isc_task_t *task, isc_event_t *event) {
query = sevent->ev_arg;
REQUIRE(DIG_VALID_QUERY(query));
+ query->started = true;
+
+ if (atomic_load(&cancel_now)) {
+ return;
+ }
+
+ INSIST(!free_now);
+
INSIST(query->waiting_connect);
query->waiting_connect = false;
@@ -4002,6 +4045,9 @@ recv_done(isc_task_t *task, isc_event_t *event) {
: "recursion not available",
query->servname);
clear_query(query);
+ if (check_if_queries_done(l, query)) {
+ cancel_lookup(l);
+ }
check_next_lookup(l);
dns_message_detach(&msg);
isc_event_free(&event);
@@ -4126,7 +4172,12 @@ recv_done(isc_task_t *task, isc_event_t *event) {
l->trace_root = false;
usesearch = false;
} else {
+ /*
+ * This is a query in the followup lookup
+ */
dighost_printmessage(query, &b, msg, true);
+
+ docancel = check_if_queries_done(l, query);
}
}
}
@@ -4305,6 +4356,7 @@ cancel_all(void) {
nq = ISC_LIST_NEXT(q, link);
debug("canceling pending query %p, belonging to %p", q,
current_lookup);
+ q->canceled = true;
if (q->sock != NULL) {
isc_socket_cancel(q->sock, NULL,
ISC_SOCKCANCEL_ALL);
@@ -4317,6 +4369,7 @@ cancel_all(void) {
nq = ISC_LIST_NEXT(q, clink);
debug("canceling connecting query %p, belonging to %p",
q, current_lookup);
+ q->canceled = true;
if (q->sock != NULL) {
isc_socket_cancel(q->sock, NULL,
ISC_SOCKCANCEL_ALL);
diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h
index 9e6035255f..48c6262008 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -166,9 +166,9 @@ struct dig_lookup {
struct dig_query {
unsigned int magic;
dig_lookup_t *lookup;
- bool waiting_connect, pending_free, waiting_senddone, first_pass,
+ bool waiting_connect, pending_free, waiting_senddone, started,
first_soa_rcvd, second_rr_rcvd, first_repeat_rcvd, recv_made,
- warn_id, timedout;
+ warn_id, timedout, canceled;
uint32_t first_rr_serial;
uint32_t second_rr_serial;
uint32_t msg_count;
diff --git a/bin/tests/system/digdelv/tests.sh b/bin/tests/system/digdelv/tests.sh
index 22933c7f9d..4b0ed7db10 100644
--- a/bin/tests/system/digdelv/tests.sh
+++ b/bin/tests/system/digdelv/tests.sh
@@ -1043,6 +1043,15 @@ if [ -x "$DIG" ] ; then
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status+ret))
+ # See [GL #3248] for more information
+ n=$((n+1))
+ echo_i "check that dig correctly refuses to use a server with a IPv4 mapped IPv6 address after failing with a regular IP address ($n)"
+ ret=0
+ dig_with_opts +nomapped @10.53.0.8 @::ffff:10.53.0.8 a.example > dig.out.test$n 2>&1 || ret=1
+ grep -F ";; Skipping mapped address" dig.out.test$n > /dev/null || ret=1
+ grep -F ";; No acceptable nameservers" dig.out.test$n > /dev/null || ret=1
+ if [ $ret -ne 0 ]; then echo_i "failed"; fi
+ status=$((status+ret))
else
echo_i "$DIG is needed, so skipping these dig tests"
fi