+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sd-messages.h>
+#include "sd-messages.h"
#include "af-list.h"
#include "alloc-util.h"
return -EOPNOTSUPP;
/* We only support the IN class */
- if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY)
+ if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY))
return -EOPNOTSUPP;
if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX)
"DNS_QUESTION=%s", key_str,
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
"DNS_SERVER=%s", dns_server_string(t->server),
- "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level),
- NULL);
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
}
/* Note that this call might invalidate the query. Callers
SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items)
dns_zone_item_notify(z);
SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done);
- if (t->probing)
+ if (t->probing && t->state == DNS_TRANSACTION_ATTEMPTS_MAX_REACHED)
(void) dns_scope_announce(t->scope, false);
SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions)
dns_server_unref(t->server);
t->server = dns_server_ref(server);
+ t->n_picked_servers ++;
+
log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id);
return 1;
dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
return 0;
-
default:
log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
goto fail;
if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
t->scope->dnssec_mode == DNSSEC_YES) {
- /* We are not in automatic downgrade mode, and the
- * server is bad, refuse operation. */
+
+ /* We are not in automatic downgrade mode, and the server is bad. Let's try a different server, maybe
+ * that works. */
+
+ if (t->n_picked_servers < dns_scope_get_n_dns_servers(t->scope)) {
+ /* We tried fewer servers on this transaction than we know, let's try another one then */
+ dns_transaction_retry(t, true);
+ return;
+ }
+
+ /* OK, let's give up, apparently all servers we tried didn't work. */
dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
return;
}
/* Request failed, immediately try again with reduced features */
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
+ * this transaction anymore, but let's see if it might make sense to send the request
+ * to a different DNS server instead. If not 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. */
+ if (t->n_picked_servers < dns_scope_get_n_dns_servers(t->scope)) {
+ /* We tried fewer servers on this transaction than we know, let's try another one then */
+ dns_transaction_retry(t, true);
+ return;
+ }
+
+ /* Give up, accept the rcode */
log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
break;
}
assert(t);
assert(t->scope);
-
switch (t->scope->protocol) {
case DNS_PROTOCOL_DNS:
/* Before trying the cache, let's make sure we figured out a
* server to use. Should this cause a change of server this
* might flush the cache. */
- dns_scope_get_dns_server(t->scope);
+ (void) dns_scope_get_dns_server(t->scope);
/* Let's then prune all outdated entries */
dns_cache_prune(&t->scope->cache);
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
bool add_known_answers = false;
DnsTransaction *other;
+ Iterator i;
+ DnsResourceKey *tkey;
+ _cleanup_set_free_ Set *keys = NULL;
unsigned qdcount;
+ unsigned nscount = 0;
usec_t ts;
int r;
if (dns_key_is_shared(t->key))
add_known_answers = true;
+ if (t->key->type == DNS_TYPE_ANY) {
+ r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(keys, t->key);
+ if (r < 0)
+ return r;
+ }
+
/*
* For mDNS, we want to coalesce as many open queries in pending transactions into one single
* query packet on the wire as possible. To achieve that, we iterate through all pending transactions
if (dns_key_is_shared(other->key))
add_known_answers = true;
+
+ if (other->key->type == DNS_TYPE_ANY) {
+ r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(keys, other->key);
+ if (r < 0)
+ return r;
+ }
}
DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
return r;
}
- t->sent = p;
- p = NULL;
+ SET_FOREACH(tkey, keys, i) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ bool tentative;
+
+ r = dns_zone_lookup(&t->scope->zone, tkey, t->scope->link->ifindex, &answer, NULL, &tentative);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_append_answer(p, answer);
+ if (r < 0)
+ return r;
+
+ nscount += dns_answer_size(answer);
+ }
+ DNS_PACKET_HEADER(p)->nscount = htobe16(nscount);
+
+ t->sent = TAKE_PTR(p);
return 0;
}
DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
DNS_PACKET_HEADER(p)->id = t->id;
- t->sent = p;
- p = NULL;
+ t->sent = TAKE_PTR(p);
return 0;
}
af_to_name_short(t->scope->family));
if (!t->initial_jitter_scheduled &&
- (t->scope->protocol == DNS_PROTOCOL_LLMNR ||
- t->scope->protocol == DNS_PROTOCOL_MDNS)) {
+ IN_SET(t->scope->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) {
usec_t jitter, accuracy;
/* RFC 4795 Section 2.7 suggests all queries should be
log_debug("Sending query via TCP since it is too large.");
else if (r == -EAGAIN)
log_debug("Sending query via TCP since server doesn't support UDP.");
- if (r == -EMSGSIZE || r == -EAGAIN)
+ if (IN_SET(r, -EMSGSIZE, -EAGAIN))
r = dns_transaction_open_tcp(t);
}
}
dns_answer_unref(t->answer);
- t->answer = validated;
- validated = NULL;
+ t->answer = TAKE_PTR(validated);
/* At this point the answer only contains validated
* RRsets. Now, let's see if it actually answers the question