summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2017-02-17 15:00:36 -0500
committerGitHub <noreply@github.com>2017-02-17 15:00:36 -0500
commitcbe8c5095856015b851925438883e0e0db541c46 (patch)
tree87b7ecd6d190ecd053f4cfd936322eab8a6390ff
parent925c81cd2074314a859ac4e99aaa763b0be46ed0 (diff)
parent6993d26469ca8deee0eea2b806b3c415deaa2e25 (diff)
Merge pull request #5347 from poettering/local-nta
more resolved fixes
-rw-r--r--src/resolve/resolve-tool.c2
-rw-r--r--src/resolve/resolved-dns-cache.c127
-rw-r--r--src/resolve/resolved-dns-dnssec.c8
-rw-r--r--src/resolve/resolved-dns-query.c12
-rw-r--r--src/resolve/resolved-dns-server.c41
-rw-r--r--src/resolve/resolved-dns-server.h2
-rw-r--r--src/resolve/resolved-dns-stub.c47
-rw-r--r--src/resolve/resolved-dns-transaction.c39
-rw-r--r--src/resolve/resolved-dns-transaction.h2
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c25
10 files changed, 235 insertions, 70 deletions
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 3cec36817a..32537ce6e8 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -38,7 +38,7 @@
#include "strv.h"
#include "terminal-util.h"
-#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
+#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC)
static int arg_family = AF_UNSPEC;
static int arg_ifindex = 0;
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index c43a7865dc..46a25c2bee 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -34,6 +34,10 @@
/* We never keep any item longer than 2h in our cache */
#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
+/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
+ * now) */
+#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC)
+
typedef enum DnsCacheItemType DnsCacheItemType;
typedef struct DnsCacheItem DnsCacheItem;
@@ -41,12 +45,14 @@ enum DnsCacheItemType {
DNS_CACHE_POSITIVE,
DNS_CACHE_NODATA,
DNS_CACHE_NXDOMAIN,
+ DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */
};
struct DnsCacheItem {
DnsCacheItemType type;
DnsResourceKey *key;
DnsResourceRecord *rr;
+ int rcode;
usec_t until;
bool authenticated:1;
@@ -60,6 +66,27 @@ struct DnsCacheItem {
LIST_FIELDS(DnsCacheItem, by_key);
};
+static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
+ assert(item);
+
+ switch (item->type) {
+
+ case DNS_CACHE_POSITIVE:
+ return "POSITIVE";
+
+ case DNS_CACHE_NODATA:
+ return "NODATA";
+
+ case DNS_CACHE_NXDOMAIN:
+ return "NXDOMAIN";
+
+ case DNS_CACHE_RCODE:
+ return dns_rcode_to_string(item->rcode);
+ }
+
+ return NULL;
+}
+
static void dns_cache_item_free(DnsCacheItem *i) {
if (!i)
return;
@@ -484,7 +511,6 @@ static int dns_cache_put_negative(
assert(c);
assert(key);
- assert(soa);
assert(owner_address);
/* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
@@ -495,13 +521,17 @@ static int dns_cache_put_negative(
if (dns_type_is_pseudo(key->type))
return 0;
- if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
- log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
- }
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
+ if (!soa)
+ return 0;
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
+ /* For negative replies, check if we have a TTL of a SOA */
+ if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
+ log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+ } else if (rcode != DNS_RCODE_SERVFAIL)
return 0;
r = dns_cache_init(c);
@@ -514,12 +544,17 @@ static int dns_cache_put_negative(
if (!i)
return -ENOMEM;
- i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
- i->until = calculate_until(soa, nsec_ttl, timestamp, true);
+ i->type =
+ rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
+ rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
+ i->until =
+ i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
+ calculate_until(soa, nsec_ttl, timestamp, true);
i->authenticated = authenticated;
i->owner_family = owner_family;
i->owner_address = *owner_address;
i->prioq_idx = PRIOQ_IDX_NULL;
+ i->rcode = rcode;
if (i->type == DNS_CACHE_NXDOMAIN) {
/* NXDOMAIN entries should apply equally to all types, so we use ANY as
@@ -543,7 +578,7 @@ static int dns_cache_put_negative(
return r;
log_debug("Added %s cache entry for %s "USEC_FMT"s",
- i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN",
+ dns_cache_item_type_to_string(i),
dns_resource_key_to_string(i->key, key_str, sizeof key_str),
(i->until - timestamp) / USEC_PER_SEC);
@@ -615,6 +650,7 @@ int dns_cache_put(
const union in_addr_union *owner_address) {
DnsResourceRecord *soa = NULL, *rr;
+ bool weird_rcode = false;
DnsAnswerFlags flags;
unsigned cache_keys;
int r, ifindex;
@@ -624,18 +660,28 @@ int dns_cache_put(
dns_cache_remove_previous(c, key, answer);
- /* We only care for positive replies and NXDOMAINs, on all
- * other replies we will simply flush the respective entries,
- * and that's it */
- if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
- return 0;
+ /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
+ * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
+ * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
+ * short time.) */
- if (dns_answer_size(answer) <= 0) {
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+ if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
- log_debug("Not caching negative entry without a SOA record: %s",
- dns_resource_key_to_string(key, key_str, sizeof key_str));
- return 0;
+ if (dns_answer_size(answer) <= 0) {
+ char key_str[DNS_RESOURCE_KEY_STRING_MAX];
+
+ log_debug("Not caching negative entry without a SOA record: %s",
+ dns_resource_key_to_string(key, key_str, sizeof key_str));
+ return 0;
+ }
+
+ } else {
+ /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
+ * beneficial. */
+ if (rcode != DNS_RCODE_SERVFAIL)
+ return 0;
+
+ weird_rcode = true;
}
cache_keys = dns_answer_size(answer);
@@ -690,19 +736,20 @@ int dns_cache_put(
if (r > 0)
return 0;
- /* See https://tools.ietf.org/html/rfc2308, which say that a
- * matching SOA record in the packet is used to enable
- * negative caching. */
+ /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
+ * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
+ * regardless of a SOA. */
r = dns_answer_find_soa(answer, key, &soa, &flags);
if (r < 0)
goto fail;
- if (r == 0)
- return 0;
-
- /* Refuse using the SOA data if it is unsigned, but the key is
- * signed */
- if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ if (r == 0 && !weird_rcode)
return 0;
+ if (r > 0) {
+ /* Refuse using the SOA data if it is unsigned, but the key is
+ * signed */
+ if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+ return 0;
+ }
r = dns_cache_put_negative(
c,
@@ -799,6 +846,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
DnsCacheItem *j, *first, *nsec = NULL;
bool have_authenticated = false, have_non_authenticated = false;
usec_t current;
+ int found_rcode = -1;
assert(c);
assert(key);
@@ -817,6 +865,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -831,6 +881,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
*ret = NULL;
*rcode = DNS_RCODE_SUCCESS;
+ *authenticated = false;
+
return 0;
}
@@ -842,6 +894,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
n++;
} else if (j->type == DNS_CACHE_NXDOMAIN)
nxdomain = true;
+ else if (j->type == DNS_CACHE_RCODE)
+ found_rcode = j->rcode;
if (j->authenticated)
have_authenticated = true;
@@ -849,6 +903,19 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod
have_non_authenticated = true;
}
+ if (found_rcode >= 0) {
+ log_debug("RCODE %s cache hit for %s",
+ dns_rcode_to_string(found_rcode),
+ dns_resource_key_to_string(key, key_str, sizeof(key_str)));
+
+ *ret = NULL;
+ *rcode = found_rcode;
+ *authenticated = false;
+
+ c->n_hit++;
+ return 1;
+ }
+
if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
/* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
* the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
@@ -1042,7 +1109,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
fputs(" -- ", f);
- fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
+ fputs(dns_cache_item_type_to_string(j), f);
fputc('\n', f);
}
}
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 51327105d0..eddab58a81 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -1710,7 +1710,8 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
}
static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
- const char *common_suffix, *wc;
+ _cleanup_free_ char *wc = NULL;
+ const char *common_suffix;
int r;
assert(rr);
@@ -1734,7 +1735,10 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
if (r <= 0)
return r;
- wc = strjoina("*.", common_suffix);
+ r = dns_name_concat("*", common_suffix, &wc);
+ if (r < 0)
+ return r;
+
return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
}
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index c58845c3b6..2b091e6c45 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* How long to wait for the query in total */
-#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
+#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC)
#define CNAME_MAX 8
#define QUERIES_MAX 2048
@@ -811,6 +811,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = 0;
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_authenticated = false;
q->answer_errno = c->error_code;
}
@@ -847,15 +848,18 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
continue;
default:
- /* Any kind of failure? Store the data away,
- * if there's nothing stored yet. */
-
+ /* Any kind of failure? Store the data away, if there's nothing stored yet. */
if (state == DNS_TRANSACTION_SUCCESS)
continue;
+ /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */
+ if (q->answer_authenticated && !t->answer_authenticated)
+ continue;
+
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_authenticated = t->answer_authenticated;
q->answer_errno = t->answer_errno;
state = t->state;
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 072cbfca1a..5498f7b9cb 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -28,7 +28,7 @@
#include "string-util.h"
/* After how much time to repeat classic DNS requests */
-#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
+#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC)
#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC)
/* The amount of time to wait before retrying with a full feature set */
@@ -399,12 +399,24 @@ static bool dns_server_grace_period_expired(DnsServer *s) {
}
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
+ DnsServerFeatureLevel best;
+
assert(s);
- if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
- dns_server_grace_period_expired(s)) {
+ /* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything
+ * better than EDNS0, hence don't even try. */
+ best = dns_server_get_dnssec_mode(s) == DNSSEC_NO ?
+ DNS_SERVER_FEATURE_LEVEL_EDNS0 :
+ DNS_SERVER_FEATURE_LEVEL_BEST;
+
+ /* Clamp the feature level the highest level we care about. The DNSSEC mode might have changed since the last
+ * time, hence let's downgrade if we are still at a higher level. */
+ if (s->possible_feature_level > best)
+ s->possible_feature_level = best;
- s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+ if (s->possible_feature_level < best && dns_server_grace_period_expired(s)) {
+
+ s->possible_feature_level = best;
dns_server_reset_counters(s);
@@ -415,6 +427,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
dns_server_feature_level_to_string(s->possible_feature_level),
dns_server_string(s));
+ dns_server_flush_cache(s);
+
} else if (s->possible_feature_level <= s->verified_feature_level)
s->possible_feature_level = s->verified_feature_level;
else {
@@ -792,6 +806,25 @@ DnssecMode dns_server_get_dnssec_mode(DnsServer *s) {
return manager_get_dnssec_mode(s->manager);
}
+void dns_server_flush_cache(DnsServer *s) {
+ DnsServer *current;
+ DnsScope *scope;
+
+ assert(s);
+
+ /* Flush the cache of the scope this server belongs to */
+
+ current = s->link ? s->link->current_dns_server : s->manager->current_dns_server;
+ if (current != s)
+ return;
+
+ scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope;
+ if (!scope)
+ return;
+
+ dns_cache_flush(&scope->cache);
+}
+
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 406282d864..bc95d53c6a 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -149,3 +149,5 @@ DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;
+
+void dns_server_flush_cache(DnsServer *s);
diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c
index 7d43825960..7afbfedfb0 100644
--- a/src/resolve/resolved-dns-stub.c
+++ b/src/resolve/resolved-dns-stub.c
@@ -94,9 +94,18 @@ static int dns_stub_finish_reply_packet(
assert(p);
- /* If the client didn't do EDNS, clamp the rcode to 4 bit */
- if (!add_opt && rcode > 0xF)
- rcode = DNS_RCODE_SERVFAIL;
+ if (!add_opt) {
+ /* If the client can't to EDNS0, don't do DO either */
+ edns0_do = false;
+
+ /* If the client didn't do EDNS, clamp the rcode to 4 bit */
+ if (rcode > 0xF)
+ rcode = DNS_RCODE_SERVFAIL;
+ }
+
+ /* Don't set the AD bit unless DO is on, too */
+ if (!edns0_do)
+ ad = false;
DNS_PACKET_HEADER(p)->id = id;
@@ -162,7 +171,7 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
return 0;
}
-static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
+static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
int r;
@@ -173,7 +182,7 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco
if (r < 0)
return log_debug_errno(r, "Failed to make failure packet: %m");
- r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), false);
+ r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), authenticated);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
@@ -198,7 +207,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
break;
}
if (r < 0) {
@@ -214,7 +223,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
q->answer_rcode,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
- DNS_PACKET_DO(q->request_dns_packet) && dns_query_fully_authenticated(q));
+ dns_query_fully_authenticated(q));
if (r < 0) {
log_debug_errno(r, "Failed to finish reply packet: %m");
break;
@@ -224,11 +233,11 @@ static void dns_stub_query_complete(DnsQuery *q) {
break;
case DNS_TRANSACTION_RCODE_FAILURE:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_NOT_FOUND:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
break;
case DNS_TRANSACTION_TIMEOUT:
@@ -244,7 +253,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
- (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
+ (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
break;
case DNS_TRANSACTION_NULL:
@@ -291,52 +300,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
log_error("Got packet on unexpected IP range, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
goto fail;
}
if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
log_debug("Got EDNS OPT field with unsupported version number.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
goto fail;
}
if (dns_type_is_obsolete(p->question->keys[0]->type)) {
log_debug("Got message with obsolete key type, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
log_debug("Got request for zone transfer, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
if (!DNS_PACKET_RD(p)) {
/* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
log_debug("Got request with recursion disabled, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
goto fail;
}
if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
log_debug("Got request with DNSSEC CD bit set, refusing.");
- dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
goto fail;
}
r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
if (r < 0) {
log_error_errno(r, "Failed to generate query object: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
@@ -356,7 +365,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
r = dns_query_go(q);
if (r < 0) {
log_error_errno(r, "Failed to start query: %m");
- dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
+ dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
goto fail;
}
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 9687409663..ecd7068683 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -31,6 +31,7 @@
#include "string-table.h"
#define TRANSACTIONS_MAX 4096
+#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
static void dns_transaction_reset_answer(DnsTransaction *t) {
assert(t);
@@ -832,7 +833,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* should hence not attempt to access the query or transaction
* after calling this function. */
- log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
+ log_debug("Processing incoming packet on transaction %" PRIu16". (rcode=%s)", t->id, dns_rcode_to_string(DNS_PACKET_RCODE(p)));
switch (t->scope->protocol) {
@@ -910,9 +911,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* Request failed, immediately try again with reduced features */
- if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) {
- /* This was already at the lowest possible feature level? If so, we can't downgrade
- * this transaction anymore, hence let's process the response, and accept the rcode. */
+ if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP) {
+ /* This was already at UDP feature level? If so, it doesn't make sense to downgrade
+ * this transaction anymore, hence let's process the response, and accept the
+ * rcode. Note that we don't retry on TCP, since that's a suitable way to mitigate
+ * packet loss, but is not going to give us better rcodes should we actually have
+ * managed to get them already at UDP level. */
+
log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
break;
}
@@ -1135,7 +1140,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
return r;
if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
- return -EAGAIN;
+ return -EAGAIN; /* Sorry, can't do UDP, try TCP! */
if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
return -EOPNOTSUPP;
@@ -1212,9 +1217,17 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t);
assert(t->scope);
+
switch (t->scope->protocol) {
case DNS_PROTOCOL_DNS:
+
+ /* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly
+ * resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that
+ * needlessly. */
+ if (t->stream)
+ return TRANSACTION_TCP_TIMEOUT_USEC;
+
assert(t->server);
return t->server->resend_timeout;
@@ -1579,7 +1592,7 @@ int dns_transaction_go(DnsTransaction *t) {
r = dns_transaction_emit_udp(t);
if (r == -EMSGSIZE)
log_debug("Sending query via TCP since it is too large.");
- if (r == -EAGAIN)
+ else if (r == -EAGAIN)
log_debug("Sending query via TCP since server doesn't support UDP.");
if (r == -EMSGSIZE || r == -EAGAIN)
r = dns_transaction_open_tcp(t);
@@ -1996,8 +2009,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r < 0)
return r;
- if (r == 0)
- continue;
+ if (r == 0) {
+ /* Hmm, so this SOA RR doesn't match our original question. In this case, maybe this is
+ * a negative reply, and we need the a SOA RR's TTL in order to cache a negative entry?
+ * If so, we need to validate it, too. */
+
+ r = dns_answer_match_key(t->answer, t->key, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) /* positive reply, we won't need the SOA and hence don't need to validate
+ * it. */
+ continue;
+ }
r = dnssec_has_rrsig(t->answer, rr->key);
if (r < 0)
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index c4fb51958d..a8d97738ef 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -178,7 +178,7 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
/* Maximum attempts to send DNS requests, across all DNS servers */
-#define DNS_TRANSACTION_ATTEMPTS_MAX 16
+#define DNS_TRANSACTION_ATTEMPTS_MAX 24
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index d8529f8317..7e08cba4e1 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -547,10 +547,33 @@ int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *ke
}
int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
+ int r;
+
assert(d);
assert(name);
- return set_contains(d->negative_by_name, name);
+ for (;;) {
+ /* If the domain is listed as-is in the NTA database, then that counts */
+ if (set_contains(d->negative_by_name, name))
+ return true;
+
+ /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
+ * 7646, section 1.1 */
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)))
+ return false;
+
+ if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
+ return false;
+
+ /* And now, let's look at the parent, and check that too */
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ }
+
+ return false;
}
static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {