]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-transaction.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-transaction.c
index 96874096636ac554759fb5a02a45e0c997856b24..a66d60540534de98933f8a10761b2cb5529f2de0 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -17,7 +18,7 @@
   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"
@@ -31,6 +32,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);
@@ -189,7 +191,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
                 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)
@@ -832,7 +834,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 +912,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 +1141,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 +1218,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;
 
@@ -1515,8 +1529,7 @@ int dns_transaction_go(DnsTransaction *t) {
                   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
@@ -1579,9 +1592,9 @@ 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)
+                if (IN_SET(r, -EMSGSIZE, -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)
@@ -2435,7 +2458,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
         if (r > 0) {
                 /* The lookup is from a TLD that is proven not to
                  * exist, and we are in downgrade mode, hence ignore
-                 * that fact that we didn't get any NSEC RRs.*/
+                 * that fact that we didn't get any NSEC RRs. */
 
                 log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.",
                          dns_resource_key_to_string(t->key, key_str, sizeof key_str));
@@ -2740,7 +2763,7 @@ static int dnssec_validate_records(
                         const char *source;
 
                         /* This RRset validated, but as a wildcard. This means we need
-                         * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/
+                         * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists. */
 
                         /* First step, determine the source of synthesis */
                         r = dns_resource_record_source(rrsig, &source);
@@ -2775,7 +2798,7 @@ static int dnssec_validate_records(
                                 return r;
                         if (r == 0) {
                                 /* Data does not require signing. In that case, just copy it over,
-                                 * but remember that this is by no means authenticated.*/
+                                 * but remember that this is by no means authenticated. */
                                 r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0);
                                 if (r < 0)
                                         return r;