]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-transaction.c
resolved: respond to local resolver requests on 127.0.0.53:53
[thirdparty/systemd.git] / src / resolve / resolved-dns-transaction.c
index a5129c201e09df4de6dcbea2b8ddb2af6b2c6274..09f60d3e76e5c9cdf2a033a6eeb6ec90e3cbeb07 100644 (file)
@@ -60,7 +60,14 @@ static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
 static void dns_transaction_close_connection(DnsTransaction *t) {
         assert(t);
 
-        t->stream = dns_stream_free(t->stream);
+        if (t->stream) {
+                /* Let's detach the stream from our transaction, in case something else keeps a reference to it. */
+                t->stream->complete = NULL;
+                t->stream->on_packet = NULL;
+                t->stream->transaction = NULL;
+                t->stream = dns_stream_unref(t->stream);
+        }
+
         t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
         t->dns_udp_fd = safe_close(t->dns_udp_fd);
 }
@@ -262,7 +269,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         if (manager_our_packet(t->scope->manager, p) != 0)
                 return;
 
-        in_addr_to_string(p->family, &p->sender, &pretty);
+        (void) in_addr_to_string(p->family, &p->sender, &pretty);
 
         log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
                   t->id,
@@ -270,7 +277,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
                   af_to_name_short(t->scope->family),
-                  pretty);
+                  strnull(pretty));
 
         /* RFC 4795, Section 4.1 says that the peer with the
          * lexicographically smaller IP address loses */
@@ -404,8 +411,12 @@ static void dns_transaction_retry(DnsTransaction *t) {
 }
 
 static int dns_transaction_maybe_restart(DnsTransaction *t) {
+        int r;
+
         assert(t);
 
+        /* Returns > 0 if the transaction was restarted, 0 if not */
+
         if (!t->server)
                 return 0;
 
@@ -420,7 +431,12 @@ static int dns_transaction_maybe_restart(DnsTransaction *t) {
 
         log_debug("Server feature level is now lower than when we began our transaction. Restarting with new ID.");
         dns_transaction_shuffle_id(t);
-        return dns_transaction_go(t);
+
+        r = dns_transaction_go(t);
+        if (r < 0)
+                return r;
+
+        return 1;
 }
 
 static int on_stream_complete(DnsStream *s, int error) {
@@ -435,7 +451,7 @@ static int on_stream_complete(DnsStream *s, int error) {
         t = s->transaction;
         p = dns_packet_ref(s->read_packet);
 
-        t->stream = dns_stream_free(t->stream);
+        dns_transaction_close_connection(t);
 
         if (ERRNO_IS_DISCONNECT(error)) {
                 usec_t usec;
@@ -547,7 +563,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
 
         r = dns_stream_write_packet(t->stream, t->sent);
         if (r < 0) {
-                t->stream = dns_stream_free(t->stream);
+                t->stream = dns_stream_unref(t->stream);
                 return r;
         }
 
@@ -557,8 +573,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
         /* The interface index is difficult to determine if we are
          * connecting to the local host, hence fill this in right away
          * instead of determining it from the socket */
-        if (t->scope->link)
-                t->stream->ifindex = t->scope->link->ifindex;
+        t->stream->ifindex = dns_scope_ifindex(t->scope);
 
         dns_transaction_reset_answer(t);
 
@@ -798,12 +813,9 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         switch (t->scope->protocol) {
 
         case DNS_PROTOCOL_LLMNR:
-                assert(t->scope->link);
+                /* For LLMNR we will not accept any packets from other interfaces */
 
-                /* For LLMNR we will not accept any packets from other
-                 * interfaces */
-
-                if (p->ifindex != t->scope->link->ifindex)
+                if (p->ifindex != dns_scope_ifindex(t->scope))
                         return;
 
                 if (p->family != t->scope->family)
@@ -820,10 +832,9 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 break;
 
         case DNS_PROTOCOL_MDNS:
-                assert(t->scope->link);
-
                 /* For mDNS we will not accept any packets from other interfaces */
-                if (p->ifindex != t->scope->link->ifindex)
+
+                if (p->ifindex != dns_scope_ifindex(t->scope))
                         return;
 
                 if (p->family != t->scope->family)
@@ -1246,7 +1257,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
          * for probing or verifying a zone item. */
         if (set_isempty(t->notify_zone_items)) {
 
-                r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL);
+                r = dns_zone_lookup(&t->scope->zone, t->key, dns_scope_ifindex(t->scope), &t->answer, NULL, NULL);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -1270,7 +1281,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
                 /* Let's then prune all outdated entries */
                 dns_cache_prune(&t->scope->cache);
 
-                r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
+                r = dns_cache_lookup(&t->scope->cache, t->key, t->clamp_ttl, &t->answer_rcode, &t->answer, &t->answer_authenticated);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -1426,6 +1437,9 @@ int dns_transaction_go(DnsTransaction *t) {
 
         assert(t);
 
+        /* Returns > 0 if the transaction is now pending, returns 0 if could be processed immediately and has finished
+         * now. */
+
         assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
 
         r = dns_transaction_prepare(t, ts);
@@ -1804,7 +1818,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
          * - For unsigned SOA/NS we get the matching DS
          * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
          * - For other unsigned RRs we get the matching SOA RR
-         * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
+         * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
+         * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
          * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
          */
 
@@ -2038,32 +2053,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                 return r;
         if (r > 0) {
                 const char *name;
+                uint16_t type = 0;
 
                 name = dns_resource_key_name(t->key);
 
-                /* If this was a SOA or NS request, then this
-                 * indicates that we are not at a zone apex, hence ask
-                 * the parent name instead. If this was a DS request,
-                 * then it's signed when the parent zone is signed,
-                 * hence ask the parent in that case, too. */
+                /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
+                 * could also be used as indication that we are not at a zone apex, but in real world setups there are
+                 * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
+                 * though they have further children. If this was a DS request, then it's signed when the parent zone
+                 * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
+                 * to see if that is signed. */
 
-                if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+                if (t->key->type == DNS_TYPE_DS) {
                         r = dns_name_parent(&name);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
-                                log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).",
+                        if (r > 0) {
+                                type = DNS_TYPE_SOA;
+                                log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
                                           t->id, dns_resource_key_name(t->key));
-                        else
+                        else
                                 name = NULL;
-                } else
+
+                } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
+
+                        type = DNS_TYPE_DS;
+                        log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
+                                  t->id, dns_resource_key_name(t->key));
+
+                } else {
+                        type = DNS_TYPE_SOA;
                         log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
                                   t->id, dns_resource_key_name(t->key));
+                }
 
                 if (name) {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
 
-                        soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name);
+                        soa = dns_resource_key_new(t->key->class, type, name);
                         if (!soa)
                                 return -ENOMEM;
 
@@ -2317,11 +2342,12 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe
 }
 
 static int dns_transaction_requires_nsec(DnsTransaction *t) {
+        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
         DnsTransaction *dt;
         const char *name;
+        uint16_t type = 0;
         Iterator i;
         int r;
-        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
 
         assert(t);
 
@@ -2355,22 +2381,25 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
 
         name = dns_resource_key_name(t->key);
 
-        if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+        if (t->key->type == DNS_TYPE_DS) {
 
-                /* We got a negative reply for this SOA/NS lookup? If
-                 * so, then we are not at a zone apex, and thus should
-                 * look at the result of the parent SOA lookup.
-                 *
-                 * We got a negative reply for this DS lookup? DS RRs
-                 * are signed when their parent zone is signed, hence
-                 * also check the parent SOA in this case. */
+                /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
+                 * hence check the parent SOA in this case. */
 
                 r = dns_name_parent(&name);
                 if (r < 0)
                         return r;
                 if (r == 0)
                         return true;
-        }
+
+                type = DNS_TYPE_SOA;
+
+        } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS))
+                /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
+                type = DNS_TYPE_DS;
+        else
+                /* For all other negative replies, check for the SOA lookup */
+                type = DNS_TYPE_SOA;
 
         /* For all other RRs we check the SOA on the same level to see
          * if it's signed. */
@@ -2379,7 +2408,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
 
                 if (dt->key->class != t->key->class)
                         continue;
-                if (dt->key->type != DNS_TYPE_SOA)
+                if (dt->key->type != type)
                         continue;
 
                 r = dns_name_equal(dns_resource_key_name(dt->key), name);