From: Christopher Faulet Date: Tue, 29 Sep 2026 07:41:15 +0000 (+0200) Subject: BUG/MINOR: resolvers: report a timeout when a TCP fallback gets no response X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=3eb4ff94f35222ceda7c65ac15a144e7eaf3fc03;p=haproxy.git BUG/MINOR: resolvers: report a timeout when a TCP fallback gets no response When a truncated UDP response triggers a TCP fallback, the resolution's status is first set to RSLV_STATUS_OTHER, then the fallback query is queued. If the TCP query never gets a response (e.g. the nameserver refuses TCP connections or doesn't respond over TCP), the resolution eventually fails on timeout, but process_resolvers() only sets the status to RSLV_STATUS_TIMEOUT when no response at all was received, which is not the case since the truncated UDP response was counted. The requesters are then notified of an "other" error: servers are put into maintenance for an "unspecified DNS error", and the "hold other" period applies instead of "hold timeout". This can be reproduced with a nameserver answering all UDP queries with the TC flag set and refusing TCP, and a server using this name with "hold other 100ms" and "hold timeout 100ms": the server goes down for "unspecified DNS error" instead of "DNS timeout status". Let's set the status to RSLV_STATUS_TIMEOUT when the fallback is queued. Any subsequent response will update it as usual, and if none comes, it is the right status to report. This was introduced in 3.5-dev6 by commit bc20f8c0b ("MEDIUM: resolvers: retry truncated UDP responses over TCP"), no backport is needed. --- diff --git a/src/resolvers.c b/src/resolvers.c index 25139c39e..eb72dd7be 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -2485,13 +2485,16 @@ static int resolv_process_responses(struct dns_nameserver *ns, enum dns_server_t break; case RSLV_RESP_TRUNCATED: - res->status = RSLV_STATUS_OTHER; ns->counters->app.resolver.truncated++; if (dns_server_type_is_dgram(type) && dns_nameserver_has_fallback(ns) && resolv_send_tcp_fallback(res, ns, - res->response_query_records[0].type)) + res->response_query_records[0].type)) { + /* if the TCP response never comes, it's a timeout */ + res->status = RSLV_STATUS_TIMEOUT; continue; + } + res->status = RSLV_STATUS_OTHER; break; case RSLV_RESP_NO_EXPECTED_RECORD: