1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

resolved: support RFC 8914 EDE error codes

If the server is able to indicate an extended error to us, using a
degraded feature set is unlikely to help.
This commit is contained in:
Ronan Pigott 2023-12-17 22:33:31 -07:00
parent 3fcd83645a
commit ac6844460c
3 changed files with 123 additions and 3 deletions

View 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);

View 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 */

View 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,6 +1301,10 @@ 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 */
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))
@ -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: