chiark / gitweb /
resolved: fix typo in sd_notify() call
[elogind.git] / src / resolve / resolved-dns-zone.c
index 04a46745f77b8cf6b439eefd658c31d63b5f9296..8098ff5e70cd14878dd206b189efe4843a8739b5 100644 (file)
@@ -28,7 +28,7 @@
 /* Never allow more than 1K entries */
 #define ZONE_MAX 1024
 
-static void dns_zone_item_probe_stop(DnsZoneItem *i) {
+void dns_zone_item_probe_stop(DnsZoneItem *i) {
         DnsTransaction *t;
         assert(i);
 
@@ -104,7 +104,7 @@ static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
         assert(rr);
 
         LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
-                if (dns_resource_record_equal(i->rr, rr))
+                if (dns_resource_record_equal(i->rr, rr) > 0)
                         return i;
 
         return NULL;
@@ -126,11 +126,11 @@ static int dns_zone_init(DnsZone *z) {
 
         assert(z);
 
-        r = hashmap_ensure_allocated(&z->by_key, dns_resource_key_hash_func, dns_resource_key_compare_func);
+        r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops);
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_allocated(&z->by_name, dns_name_hash_func, dns_name_compare_func);
+        r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops);
         if (r < 0)
                 return r;
 
@@ -194,7 +194,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                         return r;
         }
 
-        r = set_ensure_allocated(&t->zone_items, NULL, NULL);
+        r = set_ensure_allocated(&t->zone_items, NULL);
         if (r < 0)
                 goto gc;
 
@@ -493,9 +493,14 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
 
         assert(i);
 
+        if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
+                return;
+
         dns_resource_record_to_string(i->rr, &pretty);
         log_info("Detected conflict on %s", strna(pretty));
 
+        dns_zone_item_probe_stop(i);
+
         /* Withdraw the conflict item */
         i->state = DNS_ZONE_ITEM_WITHDRAWN;
 
@@ -505,6 +510,8 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
 }
 
 void dns_zone_item_ready(DnsZoneItem *i) {
+        _cleanup_free_ char *pretty = NULL;
+
         assert(i);
         assert(i->probe_transaction);
 
@@ -514,15 +521,128 @@ void dns_zone_item_ready(DnsZoneItem *i) {
         if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
                 return;
 
-        if (i->probe_transaction->state != DNS_TRANSACTION_SUCCESS) {
-                _cleanup_free_ char *pretty = NULL;
+        if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
+                bool we_lost = false;
+
+                /* The probe got a successful reply. If we so far
+                 * weren't established we just give up. If we already
+                 * were established, and the peer has the
+                 * lexicographically larger IP address we continue
+                 * and defend it. */
+
+                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 {
+                        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)
+                                log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
+                }
+
+                if (we_lost) {
+                        dns_zone_item_conflict(i);
+                        return;
+                }
+
+                log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
+        }
+
+        dns_resource_record_to_string(i->rr, &pretty);
+        log_debug("Record %s successfully probed.", strna(pretty));
+
+        dns_zone_item_probe_stop(i);
+        i->state = DNS_ZONE_ITEM_ESTABLISHED;
+}
+
+static int dns_zone_item_verify(DnsZoneItem *i) {
+        _cleanup_free_ char *pretty = NULL;
+        int r;
+
+        assert(i);
+
+        if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
+                return 0;
 
-                dns_resource_record_to_string(i->rr, &pretty);
-                log_debug("Record %s successfully probed.", strna(pretty));
+        dns_resource_record_to_string(i->rr, &pretty);
+        log_debug("Verifying RR %s", strna(pretty));
 
-                dns_zone_item_probe_stop(i);
+        i->state = DNS_ZONE_ITEM_VERIFYING;
+        r = dns_zone_item_probe_start(i);
+        if (r < 0) {
+                log_error("Failed to start probing for verifying RR: %s", strerror(-r));
                 i->state = DNS_ZONE_ITEM_ESTABLISHED;
+                return r;
+        }
 
-        } else
-                dns_zone_item_conflict(i);
+        return 0;
+}
+
+int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
+        DnsZoneItem *i, *first;
+        int c = 0;
+
+        assert(zone);
+        assert(rr);
+
+        /* This checks whether a response RR we received from somebody
+         * else is one that we actually thought was uniquely ours. If
+         * so, we'll verify our RRs. */
+
+        /* No conflict if we don't have the name at all. */
+        first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(rr->key));
+        if (!first)
+                return 0;
+
+        /* No conflict if we have the exact same RR */
+        if (dns_zone_get(zone, rr))
+                return 0;
+
+        /* OK, somebody else has RRs for the same name. Yuck! Let's
+         * start probing again */
+
+        LIST_FOREACH(by_name, i, first) {
+                if (dns_resource_record_equal(i->rr, rr))
+                        continue;
+
+                dns_zone_item_verify(i);
+                c++;
+        }
+
+        return c;
+}
+
+int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
+        DnsZoneItem *i, *first;
+        int c = 0;
+
+        assert(zone);
+
+        /* Somebody else notified us about a possible conflict. Let's
+         * verify if that's true. */
+
+        first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(key));
+        if (!first)
+                return 0;
+
+        LIST_FOREACH(by_name, i, first) {
+                dns_zone_item_verify(i);
+                c++;
+        }
+
+        return c;
+}
+
+void dns_zone_verify_all(DnsZone *zone) {
+        DnsZoneItem *i;
+        Iterator iterator;
+
+        assert(zone);
+
+        HASHMAP_FOREACH(i, zone->by_key, iterator) {
+                DnsZoneItem *j;
+
+                LIST_FOREACH(by_key, j, i)
+                        dns_zone_item_verify(j);
+        }
 }