]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: resolvers: use truncated SRV responses when no TCP fallback is possible
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 29 Sep 2026 08:53:04 +0000 (10:53 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 10 Nov 2026 14:47:52 +0000 (15:47 +0100)
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.

src/resolvers.c

index eb72dd7beee5954fc5868f5d8edd3eb69842a939..27b383f0f1ae6bd09d845e51bca0b0f5abe89d6c 100644 (file)
@@ -2402,6 +2402,7 @@ static int resolv_process_responses(struct dns_nameserver *ns, enum dns_server_t
        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) ?
@@ -2449,9 +2450,11 @@ static int resolv_process_responses(struct dns_nameserver *ns, enum dns_server_t
                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:
@@ -2494,6 +2497,14 @@ static int resolv_process_responses(struct dns_nameserver *ns, enum dns_server_t
                                        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;