]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Handle UDP send errors when sending DNS message larger than MTU
authorEvan Hunt <each@isc.org>
Tue, 22 Jun 2021 15:01:35 +0000 (17:01 +0200)
committerOndřej Surý <ondrej@sury.org>
Wed, 23 Jun 2021 15:41:34 +0000 (17:41 +0200)
When the fragmentation is disabled on UDP sockets, the uv_udp_send()
call can fail with UV_EMSGSIZE for messages larger than path MTU.
Previously, this error would end with just discarding the response.  In
this commit, a proper handling of such case is added and on such error,
a new DNS response with truncated bit set is generated and sent to the
client.

This change allows us to disable the fragmentation on the UDP
sockets again.

lib/isc/netmgr/uverr2result.c
lib/ns/client.c

index 8a5c8f669965359700d1cb336cc319f422f45d55..2c484fa1a7c6281b67023a211799d06aaefc0191 100644 (file)
@@ -85,6 +85,8 @@ isc___nm_uverr2result(int uverr, bool dolog, const char *file,
                return (ISC_R_CANCELED);
        case UV_EOF:
                return (ISC_R_EOF);
+       case UV_EMSGSIZE:
+               return (ISC_R_MAXSIZE);
        default:
                if (dolog) {
                        UNEXPECTED_ERROR(
index 814581b8fad9995a25abc175640eb1b9542f1814..5ef93af64b55a04dfcdcfffdf78b72b5d67718e5 100644 (file)
@@ -257,13 +257,33 @@ client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
        REQUIRE(client->sendhandle == handle);
 
        CTRACE("senddone");
+
+       /*
+        * Set sendhandle to NULL, but don't detach it immediately, in
+        * case we need to retry the send. If we do resend, then
+        * sendhandle will be reattached. Whether or not we resend,
+        * we will then detach the handle from *this* send by detaching
+        * 'handle' directly below.
+        */
+       client->sendhandle = NULL;
+
        if (result != ISC_R_SUCCESS) {
-               ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
-                             "send failed: %s", isc_result_totext(result));
+               if (!TCP_CLIENT(client) && result == ISC_R_MAXSIZE) {
+                       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                                     "send exceeded maximum size: truncating");
+                       client->query.attributes &= ~NS_QUERYATTR_ANSWERED;
+                       client->rcode_override = dns_rcode_noerror;
+                       ns_client_error(client, ISC_R_MAXSIZE);
+               } else {
+                       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                                     "send failed: %s",
+                                     isc_result_totext(result));
+               }
        }
 
-       isc_nmhandle_detach(&client->sendhandle);
+       isc_nmhandle_detach(&handle);
 }
 
 static void
@@ -699,8 +719,9 @@ ns_client_dropport(in_port_t port) {
 
 void
 ns_client_error(ns_client_t *client, isc_result_t result) {
+       dns_message_t *message = NULL;
        dns_rcode_t rcode;
-       dns_message_t *message;
+       bool trunc = false;
 
        REQUIRE(NS_CLIENT_VALID(client));
 
@@ -714,6 +735,10 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                rcode = (dns_rcode_t)(client->rcode_override & 0xfff);
        }
 
+       if (result == ISC_R_MAXSIZE) {
+               trunc = true;
+       }
+
 #if NS_CLIENT_DROPPORT
        /*
         * Don't send FORMERR to ports on the drop port list.
@@ -810,7 +835,11 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                        return;
                }
        }
+
        message->rcode = rcode;
+       if (trunc) {
+               message->flags |= DNS_MESSAGEFLAG_TC;
+       }
 
        if (rcode == dns_rcode_formerr) {
                /*