Commit
bc20f8c0b ("MEDIUM: resolvers: retry truncated UDP responses over
TCP") states that a truncated SRV response can still be partially
processed when no TCP fallback can be initiated for it. However for a
nameserver having both a UDP and a TCP server, the UDP response is
validated with allow_truncated_srv=0 before knowing whether the fallback
can be queued, so when resolv_send_tcp_fallback() fails (e.g. the TCP
request ring is full), the truncated SRV response is treated as an error
instead of being used, and the servers are not updated.
This was reproduced with a debug patch making resolv_send_tcp_fallback()
always fail, and a nameserver answering the SRV query with a truncated
response still carrying the SRV record and its glue: the server remained
without address before this fix, and gets 127.0.0.1 after.
Let's validate the response again allowing truncated SRV responses when
the fallback could not be queued. This is safe since the validation stops
before parsing any record for truncated responses, and rebuilds the
header and question information from scratch.
This was introduced in 3.5-dev6 by the commit above, no backport is
needed.
struct eb32_node *eb;
struct resolv_requester *req;
int keep_answer_items;
+ int allow_truncated_srv;
resolvers = ns->parent;
max_payload_size = dns_server_type_is_stream(type) ?
res->nb_responses++;
max_answer_records = (max_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE;
+ allow_truncated_srv = !(dns_server_type_is_dgram(type) &&
+ dns_nameserver_has_fallback(ns));
+ validate:
dns_resp = resolv_validate_dns_response(buf, bufend, res, max_answer_records,
- !(dns_server_type_is_dgram(type) &&
- dns_nameserver_has_fallback(ns)));
+ allow_truncated_srv);
switch (dns_resp) {
case RSLV_RESP_VALID:
res->status = RSLV_STATUS_TIMEOUT;
continue;
}
+ if (!allow_truncated_srv &&
+ res->response_query_records[0].type == DNS_RTYPE_SRV) {
+ /* no fallback possible, a truncated SRV
+ * response may still be partially used.
+ */
+ allow_truncated_srv = 1;
+ goto validate;
+ }
res->status = RSLV_STATUS_OTHER;
break;