]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: delay server feature detection
authorRonan Pigott <ronan@rjp.ie>
Wed, 20 Dec 2023 22:16:41 +0000 (15:16 -0700)
committerRonan Pigott <ronan@rjp.ie>
Thu, 4 Jan 2024 00:25:07 +0000 (17:25 -0700)
Some fields of the DnsPacket are not populated until we extract an
answer, like p->opt, despite being referenced by macros like
DNS_PACKET_RCODE. We can reorder some of the basic checks to follow
dns_packet_extract.

src/resolve/resolved-dns-transaction.c

index d44315ab1e63213448edc1bdf49103d2aae121dc..702434336c37d49eb1adb231fc8256a10803aa93 100644 (file)
@@ -1121,6 +1121,83 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 }
         }
 
+        if (DNS_PACKET_TC(p)) {
+
+                /* Truncated packets for mDNS are not allowed. Give up immediately. */
+                if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                        return;
+                }
+
+                /* Response was truncated, let's try again with good old TCP */
+                log_debug("Reply truncated, retrying via TCP.");
+                retry_with_tcp = true;
+
+        } else if (t->scope->protocol == DNS_PROTOCOL_DNS &&
+                   DNS_PACKET_IS_FRAGMENTED(p)) {
+
+                /* Report the fragment size, so that we downgrade from LARGE to regular EDNS0 if needed */
+                if (t->server)
+                        dns_server_packet_udp_fragmented(t->server, dns_packet_size_unfragmented(p));
+
+                if (t->current_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+                        /* Packet was fragmented. Let's retry with TCP to avoid fragmentation attack
+                         * issues. (We don't do that on the lowest feature level however, since crappy DNS
+                         * servers often do not implement TCP, hence falling back to TCP on fragmentation is
+                         * counter-productive there.) */
+
+                        log_debug("Reply fragmented, retrying via TCP. (Largest fragment size: %zu; Datagram size: %zu)",
+                                  p->fragsize, p->size);
+                        retry_with_tcp = true;
+                }
+        }
+
+        if (retry_with_tcp) {
+                r = dns_transaction_emit_tcp(t);
+                if (r == -ESRCH) {
+                        /* No servers found? Damn! */
+                        dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
+                        return;
+                }
+                if (r == -EOPNOTSUPP) {
+                        /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
+                        dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+                        return;
+                }
+                if (r < 0) {
+                        /* On LLMNR, if we cannot connect to the host,
+                         * we immediately give up */
+                        if (t->scope->protocol != DNS_PROTOCOL_DNS)
+                                goto fail;
+
+                        /* On DNS, couldn't send? Try immediately again, with a new server */
+                        if (dns_transaction_limited_retry(t))
+                                return;
+
+                        /* No new server to try, give up */
+                        dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
+                }
+
+                return;
+        }
+
+        /* After the superficial checks, actually parse the message. */
+        r = dns_packet_extract(p);
+        if (r < 0) {
+                if (t->server) {
+                        dns_server_packet_invalid(t->server, t->current_feature_level);
+
+                        r = dns_transaction_maybe_restart(t);
+                        if (r < 0)
+                                goto fail;
+                        if (r > 0) /* Transaction got restarted... */
+                                return;
+                }
+
+                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                return;
+        }
+
         switch (t->scope->protocol) {
 
         case DNS_PROTOCOL_DNS:
@@ -1206,83 +1283,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 assert_not_reached();
         }
 
-        if (DNS_PACKET_TC(p)) {
-
-                /* Truncated packets for mDNS are not allowed. Give up immediately. */
-                if (t->scope->protocol == DNS_PROTOCOL_MDNS) {
-                        dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
-                        return;
-                }
-
-                /* Response was truncated, let's try again with good old TCP */
-                log_debug("Reply truncated, retrying via TCP.");
-                retry_with_tcp = true;
-
-        } else if (t->scope->protocol == DNS_PROTOCOL_DNS &&
-                   DNS_PACKET_IS_FRAGMENTED(p)) {
-
-                /* Report the fragment size, so that we downgrade from LARGE to regular EDNS0 if needed */
-                if (t->server)
-                        dns_server_packet_udp_fragmented(t->server, dns_packet_size_unfragmented(p));
-
-                if (t->current_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
-                        /* Packet was fragmented. Let's retry with TCP to avoid fragmentation attack
-                         * issues. (We don't do that on the lowest feature level however, since crappy DNS
-                         * servers often do not implement TCP, hence falling back to TCP on fragmentation is
-                         * counter-productive there.) */
-
-                        log_debug("Reply fragmented, retrying via TCP. (Largest fragment size: %zu; Datagram size: %zu)",
-                                  p->fragsize, p->size);
-                        retry_with_tcp = true;
-                }
-        }
-
-        if (retry_with_tcp) {
-                r = dns_transaction_emit_tcp(t);
-                if (r == -ESRCH) {
-                        /* No servers found? Damn! */
-                        dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
-                        return;
-                }
-                if (r == -EOPNOTSUPP) {
-                        /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
-                        dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
-                        return;
-                }
-                if (r < 0) {
-                        /* On LLMNR, if we cannot connect to the host,
-                         * we immediately give up */
-                        if (t->scope->protocol != DNS_PROTOCOL_DNS)
-                                goto fail;
-
-                        /* On DNS, couldn't send? Try immediately again, with a new server */
-                        if (dns_transaction_limited_retry(t))
-                                return;
-
-                        /* No new server to try, give up */
-                        dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
-                }
-
-                return;
-        }
-
-        /* After the superficial checks, actually parse the message. */
-        r = dns_packet_extract(p);
-        if (r < 0) {
-                if (t->server) {
-                        dns_server_packet_invalid(t->server, t->current_feature_level);
-
-                        r = dns_transaction_maybe_restart(t);
-                        if (r < 0)
-                                goto fail;
-                        if (r > 0) /* Transaction got restarted... */
-                                return;
-                }
-
-                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
-                return;
-        }
-
         if (t->server) {
                 /* Report that we successfully received a valid packet with a good rcode after we initially got a bad
                  * rcode and subsequently downgraded the protocol */