]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: support RFC 8914 EDE error codes
authorRonan Pigott <ronan@rjp.ie>
Mon, 18 Dec 2023 05:33:31 +0000 (22:33 -0700)
committerRonan Pigott <ronan@rjp.ie>
Thu, 4 Jan 2024 00:25:07 +0000 (17:25 -0700)
If the server is able to indicate an extended error to us, using a
degraded feature set is unlikely to help.

src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-transaction.c

index 8eb75082cd33088f8fee65369871c7da7cddfcf0..b0b5bab6bd0fcc5aef25fcbaa555b577fac41e62 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "escape.h"
 #include "memory-util.h"
 #include "resolved-dns-packet.h"
 #include "set.h"
@@ -2570,6 +2571,78 @@ bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b) {
         return dns_packet_compare_func(a, b) == 0;
 }
 
+int dns_packet_ede_rcode(DnsPacket *p, char **ret_ede_msg) {
+        assert(p);
+
+        _cleanup_free_ char *msg = NULL, *msg_escaped = NULL;
+        int ede_rcode = _DNS_EDNS_OPT_MAX_DEFINED;
+        int r;
+        const uint8_t *d;
+        size_t l;
+
+        if (!p->opt)
+                return _DNS_EDE_RCODE_INVALID;
+
+        d = p->opt->opt.data;
+        l = p->opt->opt.data_size;
+
+        while (l > 0) {
+                uint16_t code, length;
+
+                if (l < 4U)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "EDNS0 variable part has invalid size.");
+
+                code = unaligned_read_be16(d);
+                length = unaligned_read_be16(d + 2);
+
+                if (l < 4U + length)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "Truncated option in EDNS0 variable part.");
+
+                if (code == DNS_EDNS_OPT_EXT_ERROR) {
+                        if (length < 2U)
+                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                                "EDNS0 truncated EDE info code.");
+                        ede_rcode = unaligned_read_be16(d + 4);
+                        r = make_cstring((char *)d + 6, length - 2U, MAKE_CSTRING_ALLOW_TRAILING_NUL, &msg);
+                        if (r < 0)
+                                return log_debug_errno(r, "Invalid EDE text in opt");
+                        else if (!utf8_is_valid(msg))
+                                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid EDE text in opt");
+                        else if (ede_rcode < _DNS_EDNS_OPT_MAX_DEFINED) {
+                                msg_escaped = cescape(msg);
+                                if (!msg_escaped)
+                                        return -ENOMEM;
+                        }
+                        break;
+                }
+
+                d += 4U + length;
+                l -= 4U + length;
+        }
+
+        if (ret_ede_msg)
+                *ret_ede_msg = TAKE_PTR(msg_escaped);
+
+        return ede_rcode;
+}
+
+bool dns_ede_rcode_is_dnssec(int ede_rcode) {
+        return IN_SET(ede_rcode,
+                        DNS_EDE_RCODE_UNSUPPORTED_DNSKEY_ALG,
+                        DNS_EDE_RCODE_UNSUPPORTED_DS_DIGEST,
+                        DNS_EDE_RCODE_DNSSEC_INDETERMINATE,
+                        DNS_EDE_RCODE_DNSSEC_BOGUS,
+                        DNS_EDE_RCODE_SIG_EXPIRED,
+                        DNS_EDE_RCODE_SIG_NOT_YET_VALID,
+                        DNS_EDE_RCODE_DNSKEY_MISSING,
+                        DNS_EDE_RCODE_RRSIG_MISSING,
+                        DNS_EDE_RCODE_NO_ZONE_KEY_BIT,
+                        DNS_EDE_RCODE_NSEC_MISSING
+                     );
+}
+
 int dns_packet_has_nsid_request(DnsPacket *p) {
         bool has_nsid = false;
         const uint8_t *d;
@@ -2693,7 +2766,7 @@ static const char* const dns_ede_rcode_table[_DNS_EDE_RCODE_MAX_DEFINED] = {
         [DNS_EDE_RCODE_TRANSPORT_POLICY]       = "Impossible Transport Policy",
         [DNS_EDE_RCODE_SYNTHESIZED]            = "Synthesized",
 };
-DEFINE_STRING_TABLE_LOOKUP(dns_ede_rcode, int);
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_ede_rcode, int);
 
 const char *format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) {
         const char *p = dns_ede_rcode_to_string(i);
index 497a572b04a6d2f863df85dc158b07b72dc5eb05..8f062d24ece86cd74df4290b50cbd40c1ba161e4 100644 (file)
@@ -253,6 +253,8 @@ int dns_packet_extract(DnsPacket *p);
 
 bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b);
 
+int dns_packet_ede_rcode(DnsPacket *p, char **ret_ede_msg);
+bool dns_ede_rcode_is_dnssec(int ede_rcode);
 int dns_packet_has_nsid_request(DnsPacket *p);
 
 /* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
index 702434336c37d49eb1adb231fc8256a10803aa93..0f934ef7bb6eafc9e59847e2731f45e47b37c4cc 100644 (file)
@@ -1200,11 +1200,51 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
 
         switch (t->scope->protocol) {
 
-        case DNS_PROTOCOL_DNS:
+        case DNS_PROTOCOL_DNS: {
+                int ede_rcode;
+                _cleanup_free_ char *ede_msg = NULL;
+
                 assert(t->server);
 
+                ede_rcode = dns_packet_ede_rcode(p, &ede_msg);
+                if (ede_rcode < 0 && ede_rcode != -EINVAL)
+                        log_debug_errno(ede_rcode, "Unable to extract EDE error code from packet, ignoring: %m");
+
                 if (!t->bypass &&
                     IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) {
+                        /* If the server has replied with detailed error data, using a degraded feature set
+                         * will likely not help anyone. Examine the detailed error to determine the best
+                         * course of action. */
+                        if (ede_rcode >= 0 && DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL) {
+                                /* These codes are related to DNSSEC configuration errors. If accurate,
+                                 * this is the domain operator's problem, and retrying won't help. */
+                                if (dns_ede_rcode_is_dnssec(ede_rcode)) {
+                                        log_debug("Server returned error: %s (%s%s%s). Lookup failed.",
+                                                        FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                                        FORMAT_DNS_EDE_RCODE(ede_rcode),
+                                                        isempty(ede_msg) ? "" : ": ", ede_msg);
+                                        dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+                                        return;
+                                }
+
+                                /* These codes probably indicate a transient error. Let's try again. */
+                                if (IN_SET(ede_rcode, DNS_EDE_RCODE_NOT_READY, DNS_EDE_RCODE_NET_ERROR)) {
+                                        log_debug("Server returned error: %s (%s%s%s), retrying transaction.",
+                                                        FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                                        FORMAT_DNS_EDE_RCODE(ede_rcode),
+                                                        isempty(ede_msg) ? "" : ": ", ede_msg);
+                                        dns_transaction_retry(t, false);
+                                        return;
+                                }
+
+                                /* OK, the query failed, but we still shouldn't degrade the feature set for
+                                 * this server. */
+                                log_debug("Server returned error: %s (%s%s%s)",
+                                                FORMAT_DNS_RCODE(DNS_PACKET_RCODE(p)),
+                                                FORMAT_DNS_EDE_RCODE(ede_rcode),
+                                                isempty(ede_msg) ? "" : ": ", ede_msg);
+                                break;
+                        } /* No EDE rcode, or EDE rcode we don't understand */
 
                         /* Request failed, immediately try again with reduced features */
 
@@ -1261,7 +1301,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
 
                 if (DNS_PACKET_RCODE(p) == DNS_RCODE_REFUSED) {
                         /* This server refused our request? If so, try again, use a different server */
-                        log_debug("Server returned REFUSED, switching servers, and retrying.");
+                        if (ede_rcode > 0)
+                                log_debug("Server returned REFUSED (%s), switching servers, and retrying.",
+                                                FORMAT_DNS_EDE_RCODE(ede_rcode));
+                        else
+                                log_debug("Server returned REFUSED, switching servers, and retrying.");
 
                         if (dns_transaction_limited_retry(t))
                                 return;
@@ -1273,6 +1317,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                         dns_server_packet_truncated(t->server, t->current_feature_level);
 
                 break;
+        }
 
         case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_MDNS: