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.
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: