From 0bc9811acfd2535bf8a7a16a3903a2c22df206c9 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@canonical.com>
Date: Fri, 20 Aug 2021 14:44:35 -0400
Subject: [PATCH] resolve: remove server 'large' level

This removes the DNS_SERVER_FEATURE_LEVEL_LARGE, and sets the EDNS0
advertised max packet size as if always in 'large' mode.

Without this, we always send out EDNS0 opts that limit response sizes
to 512 bytes, thus the remote server will never send anything larger
and will always truncate responses larger than 512 bytes, forcing us
to drop from EDNS0 down to TCP, even though one of the primary benefits
of EDNS0 is larger packet sizes.

Fixes: #20993
(cherry picked from commit 526fce97afe130f71dba3bd4646196bbb1188b82)

Conflict:NA
Reference:https://github.com/systemd/systemd/commit/0bc9811acfd2535bf8a7a16a3903a2c22df206c9
---
 src/resolve/resolved-dns-server.c | 63 +++++++++++--------------------
 src/resolve/resolved-dns-server.h |  3 +-
 2 files changed, 24 insertions(+), 42 deletions(-)

diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 58a1376708..a21148d288 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -282,11 +282,6 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve
         if (s->packet_bad_opt && level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
                 level = DNS_SERVER_FEATURE_LEVEL_EDNS0 - 1;
 
-        /* Even if we successfully receive a reply to a request announcing support for large packets, that
-         * does not mean we can necessarily receive large packets. */
-        if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
-                level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
-
         dns_server_verified(s, level);
 
         /* Remember the size of the largest UDP packet fragment we received from a server, we know that we
@@ -429,7 +424,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
          * better than EDNS0, hence don't even try. */
         if (dns_server_get_dnssec_mode(s) != DNSSEC_NO)
                 best = dns_server_get_dns_over_tls_mode(s) == DNS_OVER_TLS_NO ?
-                        DNS_SERVER_FEATURE_LEVEL_LARGE :
+                        DNS_SERVER_FEATURE_LEVEL_DO :
                         DNS_SERVER_FEATURE_LEVEL_TLS_DO;
         else
                 best = dns_server_get_dns_over_tls_mode(s) == DNS_OVER_TLS_NO ?
@@ -597,7 +592,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
 }
 
 int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
-        size_t packet_size;
+        size_t packet_size, udp_size;
         bool edns_do;
         int r;
 
@@ -616,40 +611,29 @@ int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeature
 
         edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
 
-        if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) {
-                size_t udp_size;
-
-                /* In large mode, advertise the local MTU, in order to avoid fragmentation (for security
-                 * reasons) – except if we are talking to localhost (where the security considerations don't
-                 * matter). If we see fragmentation, lower the reported size to the largest fragment, to
-                 * avoid it. */
-
-                udp_size = udp_header_size(server->family);
-
-                if (in_addr_is_localhost(server->family, &server->address) > 0)
-                        packet_size = 65536 - udp_size; /* force linux loopback MTU if localhost address */
-                else {
-                        /* Use the MTU pointing to the server, subtract the IP/UDP header size */
-                        packet_size = LESS_BY(dns_server_get_mtu(server), udp_size);
+        udp_size = udp_header_size(server->family);
 
-                        /* On the Internet we want to avoid fragmentation for security reasons. If we saw
-                         * fragmented packets, the above was too large, let's clamp it to the largest
-                         * fragment we saw */
-                        if (server->packet_fragmented)
-                                packet_size = MIN(server->received_udp_fragment_max, packet_size);
-
-                        /* Let's not pick ridiculously large sizes, i.e. not more than 4K. No one appears
-                         * to ever use such large sized on the Internet IRL, hence let's not either. */
-                        packet_size = MIN(packet_size, 4096U);
-                }
+        if (in_addr_is_localhost(server->family, &server->address) > 0)
+                packet_size = 65536 - udp_size; /* force linux loopback MTU if localhost address */
+        else {
+                /* Use the MTU pointing to the server, subtract the IP/UDP header size */
+                packet_size = LESS_BY(dns_server_get_mtu(server), udp_size);
+
+                /* On the Internet we want to avoid fragmentation for security reasons. If we saw
+                 * fragmented packets, the above was too large, let's clamp it to the largest
+                 * fragment we saw */
+                if (server->packet_fragmented)
+                        packet_size = MIN(server->received_udp_fragment_max, packet_size);
+
+                /* Let's not pick ridiculously large sizes, i.e. not more than 4K. No one appears
+                 * to ever use such large sized on the Internet IRL, hence let's not either. */
+                packet_size = MIN(packet_size, 4096U);
+        }
 
-                /* Strictly speaking we quite possibly can receive larger datagrams than the MTU (since the
-                 * MTU is for egress, not for ingress), but more often than not the value is symmetric, and
-                 * we want something that does the right thing in the majority of cases, and not just in the
-                 * theoretical edge case. */
-        } else
-                /* In non-large mode, let's advertise the size of the largest fragment we ever managed to accept. */
-                packet_size = server->received_udp_fragment_max;
+        /* Strictly speaking we quite possibly can receive larger datagrams than the MTU (since the
+         * MTU is for egress, not for ingress), but more often than not the value is symmetric, and
+         * we want something that does the right thing in the majority of cases, and not just in the
+         * theoretical edge case. */
 
         /* Safety clamp, never advertise less than 512 or more than 65535 */
         packet_size = CLAMP(packet_size,
@@ -1097,7 +1081,6 @@ static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVE
         [DNS_SERVER_FEATURE_LEVEL_EDNS0]     = "UDP+EDNS0",
         [DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN] = "TLS+EDNS0",
         [DNS_SERVER_FEATURE_LEVEL_DO]        = "UDP+EDNS0+DO",
-        [DNS_SERVER_FEATURE_LEVEL_LARGE]     = "UDP+EDNS0+DO+LARGE",
         [DNS_SERVER_FEATURE_LEVEL_TLS_DO]    = "TLS+EDNS0+D0",
 };
 DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index fe0eaee49c..be9efb0a79 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -32,7 +32,6 @@ typedef enum DnsServerFeatureLevel {
         DNS_SERVER_FEATURE_LEVEL_EDNS0,
         DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN,
         DNS_SERVER_FEATURE_LEVEL_DO,
-        DNS_SERVER_FEATURE_LEVEL_LARGE,
         DNS_SERVER_FEATURE_LEVEL_TLS_DO,
         _DNS_SERVER_FEATURE_LEVEL_MAX,
         _DNS_SERVER_FEATURE_LEVEL_INVALID = -EINVAL,
@@ -43,7 +42,7 @@ typedef enum DnsServerFeatureLevel {
 #define DNS_SERVER_FEATURE_LEVEL_IS_EDNS0(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
 #define DNS_SERVER_FEATURE_LEVEL_IS_TLS(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN, DNS_SERVER_FEATURE_LEVEL_TLS_DO)
 #define DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_DO)
-#define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO, DNS_SERVER_FEATURE_LEVEL_LARGE)
+#define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO)
 
 const char* dns_server_feature_level_to_string(int i) _const_;
 int dns_server_feature_level_from_string(const char *s) _pure_;
-- 
2.33.0

