]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-query.c
tree-wide: use mfree more
[thirdparty/systemd.git] / src / resolve / resolved-dns-query.c
index ef977b0948e74f050332a001694faec814019670..e03db4d003ee3630ec9a6d1fbd9a96a7ef27c5cb 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
@@ -21,6 +19,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "dns-type.h"
 #include "hostname-util.h"
 #include "local-addresses.h"
 #include "resolved-dns-query.h"
@@ -63,6 +62,7 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
 
         while ((t = set_steal_first(c->transactions))) {
                 set_remove(t->notify_query_candidates, c);
+                set_remove(t->notify_query_candidates_done, c);
                 dns_transaction_gc(t);
         }
 }
@@ -83,9 +83,7 @@ DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
         if (c->scope)
                 LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
 
-        free(c);
-
-        return NULL;
+        return mfree(c);
 }
 
 static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
@@ -93,17 +91,20 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
 
         assert(c);
 
-        if (c->search_domain && c->search_domain->linked) {
+        if (c->search_domain && c->search_domain->linked)
                 next = c->search_domain->domains_next;
+        else
+                next = dns_scope_get_search_domains(c->scope);
 
+        for (;;) {
                 if (!next) /* We hit the end of the list */
                         return 0;
 
-        } else {
-                next = dns_scope_get_search_domains(c->scope);
+                if (!next->route_only)
+                        break;
 
-                if (!next) /* OK, there's nothing. */
-                        return 0;
+                /* Skip over route-only domains */
+                next = next->domains_next;
         }
 
         dns_search_domain_unref(c->search_domain);
@@ -137,6 +138,10 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
         if (r < 0)
                 goto gc;
 
+        r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
+        if (r < 0)
+                goto gc;
+
         r = set_put(t->notify_query_candidates, c);
         if (r < 0)
                 goto gc;
@@ -147,6 +152,7 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
                 goto gc;
         }
 
+        t->clamp_ttl = c->query->clamp_ttl;
         return 1;
 
 gc:
@@ -158,6 +164,7 @@ static int dns_query_candidate_go(DnsQueryCandidate *c) {
         DnsTransaction *t;
         Iterator i;
         int r;
+        unsigned n = 0;
 
         assert(c);
 
@@ -169,8 +176,14 @@ static int dns_query_candidate_go(DnsQueryCandidate *c) {
                 r = dns_transaction_go(t);
                 if (r < 0)
                         return r;
+
+                n++;
         }
 
+        /* If there was nothing to start, then let's proceed immediately */
+        if (n == 0)
+                dns_query_candidate_notify(c);
+
         return 0;
 }
 
@@ -219,6 +232,31 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
         return state;
 }
 
+static bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) {
+        int family;
+
+        assert(c);
+
+        /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of
+         * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR,
+         * or a routable IPv6 address if we query an AAAA RR. */
+
+        if (!c->query->suppress_unroutable_family)
+                return true;
+
+        if (c->scope->protocol != DNS_PROTOCOL_DNS)
+                return true;
+
+        family = dns_type_to_af(type);
+        if (family < 0)
+                return true;
+
+        if (c->scope->link)
+                return link_relevant(c->scope->link, family, false);
+        else
+                return manager_routable(c->scope->manager, family);
+}
+
 static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
         DnsQuestion *question;
         DnsResourceKey *key;
@@ -233,14 +271,24 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
         /* Create one transaction per question key */
         DNS_QUESTION_FOREACH(key, question) {
                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
+                DnsResourceKey *qkey;
+
+                if (!dns_query_candidate_is_routable(c, key->type))
+                        continue;
 
                 if (c->search_domain) {
                         r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
                         if (r < 0)
                                 goto fail;
-                }
 
-                r = dns_query_candidate_add_transaction(c, new_key ?: key);
+                        qkey = new_key;
+                } else
+                        qkey = key;
+
+                if (!dns_scope_good_key(c->scope, qkey))
+                        continue;
+
+                r = dns_query_candidate_add_transaction(c, qkey);
                 if (r < 0)
                         goto fail;
 
@@ -354,6 +402,16 @@ DnsQuery *dns_query_free(DnsQuery *q) {
         sd_bus_message_unref(q->request);
         sd_bus_track_unref(q->bus_track);
 
+        dns_packet_unref(q->request_dns_packet);
+
+        if (q->request_dns_stream) {
+                /* Detach the stream from our query, in case something else keeps a reference to it. */
+                q->request_dns_stream->complete = NULL;
+                q->request_dns_stream->on_packet = NULL;
+                q->request_dns_stream->query = NULL;
+                dns_stream_unref(q->request_dns_stream);
+        }
+
         free(q->request_address_string);
 
         if (q->manager) {
@@ -361,9 +419,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
                 q->manager->n_dns_queries--;
         }
 
-        free(q);
-
-        return NULL;
+        return mfree(q);
 }
 
 int dns_query_new(
@@ -371,12 +427,14 @@ int dns_query_new(
                 DnsQuery **ret,
                 DnsQuestion *question_utf8,
                 DnsQuestion *question_idna,
-                int ifindex, uint64_t flags) {
+                int ifindex,
+                uint64_t flags) {
 
         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
         DnsResourceKey *key;
         bool good = false;
         int r;
+        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
 
         assert(m);
 
@@ -427,31 +485,20 @@ int dns_query_new(
         q->answer_family = AF_UNSPEC;
 
         /* First dump UTF8  question */
-        DNS_QUESTION_FOREACH(key, question_utf8) {
-                _cleanup_free_ char *p = NULL;
-
-                r = dns_resource_key_to_string(key, &p);
-                if (r < 0)
-                        return r;
-
-                log_debug("Looking up RR for %s.", strstrip(p));
-        }
+        DNS_QUESTION_FOREACH(key, question_utf8)
+                log_debug("Looking up RR for %s.",
+                          dns_resource_key_to_string(key, key_str, sizeof key_str));
 
         /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
         DNS_QUESTION_FOREACH(key, question_idna) {
-                _cleanup_free_ char *p = NULL;
-
                 r = dns_question_contains(question_utf8, key);
                 if (r < 0)
                         return r;
                 if (r > 0)
                         continue;
 
-                r = dns_resource_key_to_string(key, &p);
-                if (r < 0)
-                        return r;
-
-                log_debug("Looking up IDNA RR for %s.", strstrip(p));
+                log_debug("Looking up IDNA RR for %s.",
+                          dns_resource_key_to_string(key, key_str, sizeof key_str));
         }
 
         LIST_PREPEND(queries, m->dns_queries, q);
@@ -469,7 +516,7 @@ int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
         assert(q);
         assert(auxiliary_for);
 
-        /* Ensure that that the query is not auxiliary yet, and
+        /* Ensure that the query is not auxiliary yet, and
          * nothing else is auxiliary to it either */
         assert(!q->auxiliary_for);
         assert(!q->auxiliary_queries);
@@ -771,7 +818,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
                 switch (t->state) {
 
                 case DNS_TRANSACTION_SUCCESS: {
-                        /* We found a successfuly reply, merge it into the answer */
+                        /* We found a successfully reply, merge it into the answer */
                         r = dns_answer_extend(&q->answer, t->answer);
                         if (r < 0)
                                 goto fail;
@@ -893,7 +940,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
 
         assert(q);
 
-        q->n_cname_redirects ++;
+        q->n_cname_redirects++;
         if (q->n_cname_redirects > CNAME_MAX)
                 return -ELOOP;
 
@@ -921,6 +968,17 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
         if (r == 0 && k == 0) /* No actual cname happened? */
                 return -ELOOP;
 
+        if (q->answer_protocol == DNS_PROTOCOL_DNS) {
+                /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources
+                 * cannot invade the local namespace. The opposite way we permit: local names may redirect to global
+                 * ones. */
+
+                q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */
+        }
+
+        /* Turn off searching for the new name */
+        q->flags |= SD_RESOLVED_NO_SEARCH;
+
         dns_question_unref(q->question_idna);
         q->question_idna = nq_idna;
         nq_idna = NULL;
@@ -931,10 +989,8 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
 
         dns_query_free_candidates(q);
         dns_query_reset_answer(q);
-        q->state = DNS_TRANSACTION_NULL;
 
-        /* Turn off searching for the new name */
-        q->flags |= SD_RESOLVED_NO_SEARCH;
+        q->state = DNS_TRANSACTION_NULL;
 
         return 0;
 }