#include "alloc-util.h"
#include "dns-domain.h"
+#include "escape.h"
#include "memory-util.h"
#include "resolved-dns-packet.h"
#include "set.h"
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;
[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);
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 */
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 */
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;
dns_server_packet_truncated(t->server, t->current_feature_level);
break;
+ }
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS: