]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-dns-zone.c
resolved: rework parsing of /etc/hosts
[thirdparty/systemd.git] / src / resolve / resolved-dns-zone.c
index 850eed8cb8414f6b5a8b03f58ab9358031f1f8ff..c2d9f3d33d5dda8d969ad3c8ccb47717c5a11a89 100644 (file)
@@ -1,27 +1,11 @@
-/***
-  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/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
 #include "dns-domain.h"
 #include "list.h"
 #include "resolved-dns-packet.h"
 #include "resolved-dns-zone.h"
+#include "resolved-dnssd.h"
 #include "string-util.h"
 
 /* Never allow more than 1K entries */
@@ -34,8 +18,7 @@ void dns_zone_item_probe_stop(DnsZoneItem *i) {
         if (!i->probe_transaction)
                 return;
 
-        t = i->probe_transaction;
-        i->probe_transaction = NULL;
+        t = TAKE_PTR(i->probe_transaction);
 
         set_remove(t->notify_zone_items, i);
         set_remove(t->notify_zone_items_done, i);
@@ -94,7 +77,7 @@ void dns_zone_flush(DnsZone *z) {
         z->by_name = hashmap_free(z->by_name);
 }
 
-static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
+DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
         DnsZoneItem *i;
 
         assert(z);
@@ -118,6 +101,22 @@ void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
                 dns_zone_item_remove_and_free(z, i);
 }
 
+int dns_zone_remove_rrs_by_key(DnsZone *z, DnsResourceKey *key) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+        DnsResourceRecord *rr;
+        bool tentative;
+        int r;
+
+        r = dns_zone_lookup(z, key, 0, &answer, &soa, &tentative);
+        if (r < 0)
+                return r;
+
+        DNS_ANSWER_FOREACH(rr, answer)
+                dns_zone_remove_rr(z, rr);
+
+        return 0;
+}
+
 static int dns_zone_init(DnsZone *z) {
         int r;
 
@@ -196,6 +195,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                 goto gc;
 
         i->probe_transaction = t;
+        t->probing = true;
 
         if (t->state == DNS_TRANSACTION_NULL) {
 
@@ -287,13 +287,34 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
         return 0;
 }
 
-int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
+static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int ifindex) {
+        DnsAnswerFlags flags;
+
+        /* From RFC 6762, Section 10.2
+         * "They (the rules about when to set the cache-flush bit) apply to
+         * startup announcements as described in Section 8.3, "Announcing",
+         * and to responses generated as a result of receiving query messages."
+         * So, set the cache-flush bit for mDNS answers except for DNS-SD
+         * service enumeration PTRs described in RFC 6763, Section 4.1. */
+        if (i->scope->protocol == DNS_PROTOCOL_MDNS &&
+            !dns_resource_key_is_dnssd_ptr(i->rr->key))
+                flags = DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHE_FLUSH;
+        else
+                flags = DNS_ANSWER_AUTHENTICATED;
+
+        return dns_answer_add(a, i->rr, ifindex, flags);
+}
+
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
         unsigned n_answer = 0;
         DnsZoneItem *j, *first;
         bool tentative = true, need_soa = false;
         int r;
 
+        /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the
+         * ifindex field in the answer with it */
+
         assert(z);
         assert(key);
         assert(ret_answer);
@@ -389,7 +410,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                         if (k < 0)
                                 return k;
                         if (k > 0) {
-                                r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
+                                r = dns_zone_add_authenticated_answer(answer, j, ifindex);
                                 if (r < 0)
                                         return r;
 
@@ -398,7 +419,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                 }
 
                 if (found && !added) {
-                        r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL);
+                        r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
                         if (r < 0)
                                 return r;
                 }
@@ -415,7 +436,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                         if (j->state != DNS_ZONE_ITEM_PROBING)
                                 tentative = false;
 
-                        r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
+                        r = dns_zone_add_authenticated_answer(answer, j, ifindex);
                         if (r < 0)
                                 return r;
                 }
@@ -435,7 +456,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                         }
 
                         if (add_soa) {
-                                r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL);
+                                r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
                                 if (r < 0)
                                         return r;
                         }
@@ -448,13 +469,10 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
         if (!ret_tentative && tentative)
                 goto return_empty;
 
-        *ret_answer = answer;
-        answer = NULL;
+        *ret_answer = TAKE_PTR(answer);
 
-        if (ret_soa) {
-                *ret_soa = soa;
-                soa = NULL;
-        }
+        if (ret_soa)
+                *ret_soa = TAKE_PTR(soa);
 
         if (ret_tentative)
                 *ret_tentative = tentative;
@@ -486,6 +504,8 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
         /* Withdraw the conflict item */
         i->state = DNS_ZONE_ITEM_WITHDRAWN;
 
+        dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
+
         /* Maybe change the hostname */
         if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
                 manager_next_hostname(i->scope->manager);
@@ -505,7 +525,9 @@ void dns_zone_item_notify(DnsZoneItem *i) {
                 bool we_lost = false;
 
                 /* The probe got a successful reply. If we so far
-                 * weren't established we just give up. If we already
+                 * weren't established we just give up.
+                 *
+                 * In LLMNR case if we already
                  * were established, and the peer has the
                  * lexicographically larger IP address we continue
                  * and defend it. */
@@ -513,7 +535,7 @@ void dns_zone_item_notify(DnsZoneItem *i) {
                 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
                         log_debug("Got a successful probe for not yet established RR, we lost.");
                         we_lost = true;
-                } else {
+                } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
                         assert(i->probe_transaction->received);
                         we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
                         if (we_lost)
@@ -575,6 +597,10 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
         if (dns_zone_get(zone, rr))
                 return 0;
 
+        /* No conflict if it is DNS-SD RR used for service enumeration. */
+        if (dns_resource_key_is_dnssd_ptr(rr->key))
+                return 0;
+
         /* OK, somebody else has RRs for the same name. Yuck! Let's
          * start probing again */
 
@@ -659,3 +685,20 @@ bool dns_zone_is_empty(DnsZone *zone) {
 
         return hashmap_isempty(zone->by_key);
 }
+
+bool dns_zone_contains_name(DnsZone *z, const char *name) {
+        DnsZoneItem *i, *first;
+
+        first = hashmap_get(z->by_name, name);
+        if (!first)
+                return false;
+
+        LIST_FOREACH(by_name, i, first) {
+                if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                        continue;
+
+                return true;
+        }
+
+        return false;
+}