mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
commit
cbe8c50958
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
s->possible_feature_level = 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;
|
||||
|
||||
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",
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user