X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-zone.c;h=8098ff5e70cd14878dd206b189efe4843a8739b5;hp=d4e05528870ee31baa74bac0607c81427e29f23b;hb=b37d45c9ab5f645502695e47d268af1a54216e0e;hpb=cd1b20f90abb1e49d60d8c3f4a7665ca93bea436 diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index d4e055288..8098ff5e7 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -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; @@ -187,14 +187,14 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (r < 0) return r; - t = dns_scope_find_transaction(i->scope, question); + t = dns_scope_find_transaction(i->scope, question, false); if (!t) { r = dns_transaction_new(&t, i->scope, question); if (r < 0) return r; } - r = set_ensure_allocated(&t->zone_items, NULL, NULL); + r = set_ensure_allocated(&t->zone_items, NULL); if (r < 0) goto gc; @@ -279,14 +279,14 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { if (established) i->state = DNS_ZONE_ITEM_ESTABLISHED; else { + i->state = DNS_ZONE_ITEM_PROBING; + r = dns_zone_item_probe_start(i); if (r < 0) { dns_zone_item_remove_and_free(z, i); i = NULL; return r; } - - i->state = DNS_ZONE_ITEM_PROBING; } } else i->state = DNS_ZONE_ITEM_ESTABLISHED; @@ -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_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; - dns_zone_item_probe_stop(i); + assert(i); + + if (i->state != DNS_ZONE_ITEM_ESTABLISHED) + return 0; + + dns_resource_record_to_string(i->rr, &pretty); + log_debug("Verifying RR %s", strna(pretty)); + + 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); + } }