chg: dev: Use a suitable response in tcp_connected() when initiating a read

When 'ISC_R_TIMEDOUT' is received in 'tcp_recv()', it times out the
oldest response in the active responses queue, and only after that it
checks whether other active responses have also timed out. So when
setting a timeout value for a read operation after a successful
connection, it makes sense to take the timeout value from the oldest
response in the active queue too, because, theoretically, the responses
can have different timeout values, e.g. when the TCP dispatch is shared.
Currently 'resp' is always NULL. Previously when connect and read timeouts
were not separated in dispatch this affected only logging, but now since
we are setting a new timeout after a successful connection, we need to
choose a suitable response from the active queue.

Merge branch 'aram/dispatch-tcp_connected-fix' into 'main'

See merge request isc-projects/bind9!9927
This commit is contained in:
Arаm Sаrgsyаn 2025-01-22 13:41:25 +00:00
commit 66d4f9184a
3 changed files with 69 additions and 69 deletions

View file

@ -53,16 +53,14 @@ grep -F "no servers could be reached" dig.out.ns1.test${n} >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
# 'resolver-query-timeout' is set to 5 seconds in ns1, which is lower than the
# current single query timeout value MAX_SINGLE_QUERY_TIMEOUT of 9 seconds, so
# the "hung fetch" timer should kick in, interrupt the non-responsive query and
# send a SERVFAIL answer.
# 'resolver-query-timeout' is set to 5 seconds in ns1, so named should
# interrupt the non-responsive query and send a SERVFAIL answer before dig's
# own timeout fires, which is set to 7 seconds.
n=$((n + 1))
echo_i "checking no response handling with a longer than resolver-query-timeout timeout ($n)"
ret=0
dig_with_opts +tcp +tries=1 +timeout=7 noresponse.example.net @10.53.0.1 a >dig.out.ns1.test${n} || ret=1
grep -F "status: SERVFAIL" dig.out.ns1.test${n} >/dev/null || ret=1
grep -F "EDE: 22 (No Reachable Authority)" dig.out.ns1.test${n} >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))

View file

@ -1871,18 +1871,17 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
}
}
if (ISC_LIST_EMPTY(disp->active)) {
/* Take the oldest active response. */
resp = ISC_LIST_HEAD(disp->active);
if (resp == NULL) {
/* All responses have been canceled */
disp->state = DNS_DISPATCHSTATE_CANCELED;
} else if (eresult == ISC_R_SUCCESS) {
disp->state = DNS_DISPATCHSTATE_CONNECTED;
isc_nmhandle_attach(handle, &disp->handle);
if (resp != NULL) {
isc_nmhandle_cleartimeout(disp->handle);
if (resp->timeout != 0) {
isc_nmhandle_settimeout(disp->handle,
resp->timeout);
}
isc_nmhandle_cleartimeout(disp->handle);
if (resp->timeout != 0) {
isc_nmhandle_settimeout(disp->handle, resp->timeout);
}
tcp_startrecv(disp, resp);
} else {

View file

@ -360,7 +360,6 @@ struct fetchctx {
atomic_uint_fast32_t attributes;
isc_timer_t *timer;
isc_time_t expires;
isc_time_t next_timeout;
isc_interval_t interval;
dns_message_t *qmessage;
ISC_LIST(resquery_t) queries;
@ -1875,7 +1874,6 @@ fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
seconds = us / US_PER_SEC;
us -= seconds * US_PER_SEC;
isc_interval_set(&fctx->interval, seconds, us * NS_PER_US);
isc_time_nowplusinterval(&fctx->next_timeout, &fctx->interval);
}
static isc_result_t
@ -1911,6 +1909,66 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
}
}
/*
* Maybe apply DNS64 mappings to IPv4 addresses.
*/
sockaddr = addrinfo->sockaddr;
dns64 = ISC_LIST_HEAD(fctx->res->view->dns64);
if (isc_sockaddr_pf(&sockaddr) == AF_INET &&
fctx->res->view->usedns64 && dns64 != NULL)
{
struct in6_addr aaaa;
result = dns_dns64_aaaafroma(
dns64, NULL, NULL, fctx->res->view->aclenv, 0,
(unsigned char *)&sockaddr.type.sin.sin_addr.s_addr,
aaaa.s6_addr);
if (result == ISC_R_SUCCESS) {
char sockaddrbuf1[ISC_SOCKADDR_FORMATSIZE];
char sockaddrbuf2[ISC_SOCKADDR_FORMATSIZE];
/* format old address */
isc_sockaddr_format(&sockaddr, sockaddrbuf1,
sizeof(sockaddrbuf1));
/* replace address */
isc_sockaddr_fromin6(&sockaddr, &aaaa,
ntohs(sockaddr.type.sin.sin_port));
addrinfo->sockaddr = sockaddr;
/* format new address */
isc_sockaddr_format(&sockaddr, sockaddrbuf2,
sizeof(sockaddrbuf2));
isc_log_write(DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
"Using DNS64 address %s to talk to %s\n",
sockaddrbuf2, sockaddrbuf1);
}
}
/*
* Check if the address is in the peers list and has a special
* confguration.
*/
if (res->view->peers != NULL) {
dns_peer_t *peer = NULL;
isc_netaddr_t dstip;
bool usetcp = false;
isc_netaddr_fromsockaddr(&dstip, &sockaddr);
result = dns_peerlist_peerbyaddr(res->view->peers, &dstip,
&peer);
if (result == ISC_R_SUCCESS) {
result = dns_peer_getquerysource(peer, &addr);
if (result == ISC_R_SUCCESS) {
have_addr = true;
}
result = dns_peer_getforcetcp(peer, &usetcp);
if (result == ISC_R_SUCCESS && usetcp) {
options |= DNS_FETCHOPT_TCP;
}
}
}
/*
* Allow an additional second for the kernel to resend the SYN
* (or SYN without ECN in the case of stupid firewalls blocking
@ -1960,61 +2018,6 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
DNS_MESSAGE_INTENTPARSE, &query->rmessage);
query->start = isc_time_now();
/*
* Maybe apply DNS64 mappings to IPv4 addresses.
*/
sockaddr = addrinfo->sockaddr;
dns64 = ISC_LIST_HEAD(fctx->res->view->dns64);
if (isc_sockaddr_pf(&sockaddr) == AF_INET &&
fctx->res->view->usedns64 && dns64 != NULL)
{
struct in6_addr aaaa;
result = dns_dns64_aaaafroma(
dns64, NULL, NULL, fctx->res->view->aclenv, 0,
(unsigned char *)&sockaddr.type.sin.sin_addr.s_addr,
aaaa.s6_addr);
if (result == ISC_R_SUCCESS) {
char sockaddrbuf1[ISC_SOCKADDR_FORMATSIZE];
char sockaddrbuf2[ISC_SOCKADDR_FORMATSIZE];
/* format old address */
isc_sockaddr_format(&sockaddr, sockaddrbuf1,
sizeof(sockaddrbuf1));
/* replace address */
isc_sockaddr_fromin6(&sockaddr, &aaaa,
ntohs(sockaddr.type.sin.sin_port));
addrinfo->sockaddr = sockaddr;
/* format new address */
isc_sockaddr_format(&sockaddr, sockaddrbuf2,
sizeof(sockaddrbuf2));
isc_log_write(DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
"Using DNS64 address %s to talk to %s\n",
sockaddrbuf2, sockaddrbuf1);
}
}
if (res->view->peers != NULL) {
dns_peer_t *peer = NULL;
isc_netaddr_t dstip;
bool usetcp = false;
isc_netaddr_fromsockaddr(&dstip, &sockaddr);
result = dns_peerlist_peerbyaddr(res->view->peers, &dstip,
&peer);
if (result == ISC_R_SUCCESS) {
result = dns_peer_getquerysource(peer, &addr);
if (result == ISC_R_SUCCESS) {
have_addr = true;
}
result = dns_peer_getforcetcp(peer, &usetcp);
if (result == ISC_R_SUCCESS && usetcp) {
query->options |= DNS_FETCHOPT_TCP;
}
}
}
/*
* If this is a TCP query, then we need to make a socket and
* a dispatch for it here. Otherwise we use the resolver's