[systemd-commits] 13 commits - TODO man/resolved.conf.xml man/systemd-resolved.service.xml man/systemd-timesyncd.service.xml src/resolve src/shared src/timesync

Lennart Poettering lennart at kemper.freedesktop.org
Mon Aug 11 06:06:31 PDT 2014


 TODO                                   |    6 -
 man/resolved.conf.xml                  |    4 
 man/systemd-resolved.service.xml       |   16 +-
 man/systemd-timesyncd.service.xml      |   10 +
 src/resolve/resolved-bus.c             |   52 ++++++++
 src/resolve/resolved-dns-cache.c       |   79 ++++++++++++-
 src/resolve/resolved-dns-cache.h       |    4 
 src/resolve/resolved-dns-packet.c      |    7 +
 src/resolve/resolved-dns-packet.h      |    2 
 src/resolve/resolved-dns-query.c       |   31 +++++
 src/resolve/resolved-dns-query.h       |    4 
 src/resolve/resolved-dns-scope.c       |  198 ++++++++++++++++++++++++++++++++-
 src/resolve/resolved-dns-scope.h       |    8 +
 src/resolve/resolved-dns-transaction.c |   42 +++++--
 src/resolve/resolved-dns-zone.c        |  137 +++++++++++++++++++++-
 src/resolve/resolved-dns-zone.h        |    7 +
 src/resolve/resolved-manager.c         |  111 +++++++++++-------
 src/resolve/resolved-manager.h         |    8 +
 src/shared/bus-errors.h                |    1 
 src/timesync/timesyncd.c               |   16 +-
 20 files changed, 650 insertions(+), 93 deletions(-)

New commits:
commit 6a5c7b7e41a036bfdc2474b4583fcfdd358a6db6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 23:40:48 2014 +0200

    timesyncd: always use CLOCK_BOOTTIME if we can
    
    After all we want to compare a monotonically increasing clock with the
    remote clock, hence we shouldn't ignore system suspend periods.

diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 1bd8cf5..ff76f97 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -245,7 +245,7 @@ static int manager_send_request(Manager *m) {
          * The actual value does not matter, We do not care about the correct
          * NTP UINT_MAX fraction; we just pass the plain nanosecond value.
          */
-        assert_se(clock_gettime(CLOCK_MONOTONIC, &m->trans_time_mon) >= 0);
+        assert_se(clock_gettime(clock_boottime_or_monotonic(), &m->trans_time_mon) >= 0);
         assert_se(clock_gettime(CLOCK_REALTIME, &m->trans_time) >= 0);
         ntpmsg.trans_time.sec = htobe32(m->trans_time.tv_sec + OFFSET_1900_1970);
         ntpmsg.trans_time.frac = htobe32(m->trans_time.tv_nsec);
@@ -277,8 +277,8 @@ static int manager_send_request(Manager *m) {
         r = sd_event_add_time(
                         m->event,
                         &m->event_timeout,
-                        CLOCK_MONOTONIC,
-                        now(CLOCK_MONOTONIC) + TIMEOUT_USEC, 0,
+                        clock_boottime_or_monotonic(),
+                        now(clock_boottime_or_monotonic()) + TIMEOUT_USEC, 0,
                         manager_timeout, m);
         if (r < 0) {
                 log_error("Failed to arm timeout timer: %s", strerror(-r));
@@ -308,7 +308,7 @@ static int manager_arm_timer(Manager *m, usec_t next) {
         }
 
         if (m->event_timer) {
-                r = sd_event_source_set_time(m->event_timer, now(CLOCK_MONOTONIC) + next);
+                r = sd_event_source_set_time(m->event_timer, now(clock_boottime_or_monotonic()) + next);
                 if (r < 0)
                         return r;
 
@@ -318,8 +318,8 @@ static int manager_arm_timer(Manager *m, usec_t next) {
         return sd_event_add_time(
                         m->event,
                         &m->event_timer,
-                        CLOCK_MONOTONIC,
-                        now(CLOCK_MONOTONIC) + next, 0,
+                        clock_boottime_or_monotonic(),
+                        now(clock_boottime_or_monotonic()) + next, 0,
                         manager_timer, m);
 }
 
@@ -685,7 +685,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
          *  The round-trip delay, d, and system clock offset, t, are defined as:
          *  d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2"
          */
-        assert_se(clock_gettime(CLOCK_MONOTONIC, &now_ts) >= 0);
+        assert_se(clock_gettime(clock_boottime_or_monotonic(), &now_ts) >= 0);
         origin = tv_to_d(recv_time) - (ts_to_d(&now_ts) - ts_to_d(&m->trans_time_mon)) + OFFSET_1900_1970;
         receive = ntp_ts_to_d(&ntpmsg.recv_time);
         trans = ntp_ts_to_d(&ntpmsg.trans_time);
@@ -912,7 +912,7 @@ static int manager_connect(Manager *m) {
         if (!ratelimit_test(&m->ratelimit)) {
                 log_debug("Slowing down attempts to contact servers.");
 
-                r = sd_event_add_time(m->event, &m->event_retry, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + RETRY_USEC, 0, manager_retry, m);
+                r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + RETRY_USEC, 0, manager_retry, m);
                 if (r < 0) {
                         log_error("Failed to create retry timer: %s", strerror(-r));
                         return r;

commit 31a339fd7f0e87376bade9ebd7ae32c58d34cf85
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 23:40:18 2014 +0200

    man: update resolved man pages a bit

diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index 6e0185b..c582368 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -56,8 +56,8 @@
 
                 <para>When starting, systemd-resolved will read the
                 configuration file <filename>resolved.conf</filename>.
-                This configuration file controls local DNS name
-                resolving.</para>
+                This configuration file controls local DNS and LLMNR
+                name resolving.</para>
 
         </refsect1>
 
diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml
index 839f46a..dc0e3b6 100644
--- a/man/systemd-resolved.service.xml
+++ b/man/systemd-resolved.service.xml
@@ -57,13 +57,17 @@
                 <title>Description</title>
 
                 <para><command>systemd-resolved</command> is a system
-                service that manages network name resolution. It does so by
-                generating <filename>/run/systemd/resolve/resolv.conf</filename>,
-                which may be symlinked from <filename>/etc/resolv.conf</filename>.
-                The contents is generated from the global settings in
+                service that manages network name resolution. It
+                implements a caching DNS stub resolver and an LLMNR
+                resolver and responder. It also generates
+                <filename>/run/systemd/resolve/resolv.conf</filename>
+                for compatibility which may be symlinked from
+                <filename>/etc/resolv.conf</filename>.  The DNS servers contacted
+                are determined from the global settings in
                 <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-                the per-link static settings in <filename>.network</filename> files,
-                and the per-link dynamic settings received over DHCP. See
+                the per-link static settings in
+                <filename>.network</filename> files, and the per-link
+                dynamic settings received over DHCP. See
                 <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
                 for more details.</para>
 

commit 81663503e6769b4634b7d717ec8f112e26423f6f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 23:36:41 2014 +0200

    man: extend timesycnd man page a bit

diff --git a/man/systemd-timesyncd.service.xml b/man/systemd-timesyncd.service.xml
index 3810feb..9647c54 100644
--- a/man/systemd-timesyncd.service.xml
+++ b/man/systemd-timesyncd.service.xml
@@ -57,8 +57,14 @@
                 <title>Description</title>
 
                 <para><filename>systemd-timesyncd</filename> is a
-                system service that may be used to synchronize the local
-                system clock with a Network Time Protocol Server.</para>
+                system service that may be used to synchronize the
+                local system clock with a remote Network Time Protocol
+                server. It also saves the local time to disk every
+                time the clock has been synchronized and uses this to
+                possibly advance the system realtime clock on
+                subsequent reboots to ensure it
+                monotonically advances even if the system lacks a
+                battery-buffered RTC chip.</para>
         </refsect1>
 
         <refsect1>

commit 556a22945fcc88ca27ae7ecc46c9bb2727e37895
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 23:10:08 2014 +0200

    resolved: when picking a new hostname make sure two hosts pick different ones
    
    This way we can avoid always picking the same replacement hostnames when
    picking one.

diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 1d33c2a..988aa6e 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -1646,7 +1646,7 @@ void manager_refresh_rrs(Manager *m) {
 
 int manager_next_hostname(Manager *m) {
         const char *p;
-        uint64_t u;
+        uint64_t u, a;
         char *h;
 
         assert(m);
@@ -1664,7 +1664,15 @@ int manager_next_hostname(Manager *m) {
         if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0)
                 u = 1;
 
-        u++;
+        /* Add a random number to the old value. This way we can avoid
+         * that two hosts pick the same hostname, win on IPv4 and lose
+         * on IPv6 (or vice versa), and pick the same hostname
+         * replacement hostname, ad infinitum. We still want the
+         * numbers to go up monotonically, hence we just add a random
+         * value 1..10 */
+
+        random_bytes(&a, sizeof(a));
+        u += 1 + a % 10;
 
         if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
                 return -ENOMEM;

commit 4d91eec42d3ba547c4e2578df0d6fd568075647b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 22:48:16 2014 +0200

    resolved: actually, the peer with the lower IP address wins conflicts

diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index dfed74d..990b1f2 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -137,12 +137,12 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
 
         /* RFC 4795, Section 4.1 says that the peer with the
          * lexicographically smaller IP address loses */
-        if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) < 0) {
-                log_debug("Peer has lexicographically smaller IP address and thus lost in the conflict.");
+        if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
+                log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
                 return;
         }
 
-        log_debug("We have the lexicographically smaller IP address and thus lost in the conflict.");
+        log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
 
         t->block_gc++;
         while ((z = set_first(t->zone_items))) {
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index ebbd1e2..370ecef 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -527,7 +527,7 @@ void dns_zone_item_ready(DnsZoneItem *i) {
                 /* 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 smaller IP address we continue
+                 * lexicographically larger IP address we continue
                  * and defend it. */
 
                 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
@@ -535,9 +535,9 @@ void dns_zone_item_ready(DnsZoneItem *i) {
                         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;
+                        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 lower IP address and thus 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) {

commit 3ef64445cdf12d7703aa79b39f3c170037d587c7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Aug 10 22:28:12 2014 +0200

    resolved: make sure we don't mark the wrong zone RRs conflicting

diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index cf2987d..dfed74d 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -120,7 +120,6 @@ static void dns_transaction_stop(DnsTransaction *t) {
 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         _cleanup_free_ char *pretty = NULL;
         DnsZoneItem *z;
-        Iterator i;
 
         assert(t);
         assert(p);
@@ -146,8 +145,15 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         log_debug("We have the lexicographically smaller IP address and thus lost in the conflict.");
 
         t->block_gc++;
-        SET_FOREACH(z, t->zone_items, i)
+        while ((z = set_first(t->zone_items))) {
+                /* First, make sure the zone item drops the reference
+                 * to us */
+                dns_zone_item_probe_stop(z);
+
+                /* Secondly, report this as conflict, so that we might
+                 * look for a different hostname */
                 dns_zone_item_conflict(z);
+        }
         t->block_gc--;
 
         dns_transaction_gc(t);
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 13b1b3c..ebbd1e2 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);
 
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index 482df2f..7185126 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -76,3 +76,5 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
 int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
 
 void dns_zone_verify_all(DnsZone *zone);
+
+void dns_zone_item_probe_stop(DnsZoneItem *i);

commit 2fb3034cb21c745ed4f9aa4cba57563f7f071466
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 17:21:00 2014 +0200

    resolved: be a bit more communicative about conflicts

diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index e76940e..cf2987d 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -118,6 +118,7 @@ static void dns_transaction_stop(DnsTransaction *t) {
 }
 
 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
+        _cleanup_free_ char *pretty = NULL;
         DnsZoneItem *z;
         Iterator i;
 
@@ -127,10 +128,13 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         if (manager_our_packet(t->scope->manager, p) != 0)
                 return;
 
-        log_debug("Transaction on scope %s on %s/%s got tentative packet",
+        in_addr_to_string(p->family, &p->sender, &pretty);
+
+        log_debug("Transaction on scope %s on %s/%s got tentative packet from %s",
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
-                  t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
+                  t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
+                  pretty);
 
         /* RFC 4795, Section 4.1 says that the peer with the
          * lexicographically smaller IP address loses */
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 5f250e7..13b1b3c 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -530,11 +530,14 @@ void dns_zone_item_ready(DnsZoneItem *i) {
                  * lexicographically smaller IP address we continue
                  * and defend it. */
 
-                if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                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 {
                         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 lower IP address and thus lost.");
                 }
 
                 if (we_lost) {
@@ -553,6 +556,7 @@ void dns_zone_item_ready(DnsZoneItem *i) {
 }
 
 static int dns_zone_item_verify(DnsZoneItem *i) {
+        _cleanup_free_ char *pretty = NULL;
         int r;
 
         assert(i);
@@ -560,6 +564,9 @@ static int dns_zone_item_verify(DnsZoneItem *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) {

commit c02091d23bd746c176220e8492eeb7c4c62c3e55
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 17:00:14 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index f624397..067691f 100644
--- a/TODO
+++ b/TODO
@@ -33,12 +33,6 @@ Features:
         - use base64 for key presentation?
         - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)?
         - add nice formatting of DNS timestamps
-  - LLMNR:
-        - process incoming notification of conflict
-        - send notifications of conflict
-        - detect conflicts
-        - collect multiple responses
-        - reprobe after suspend
   - DNS
         - search paths
   - mDNS/DNS-SD

commit 902bb5d8abb2a7d258741828d212ca549ab16950
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 16:59:48 2014 +0200

    resolved: verify all RRs when we come back from suspend

diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 6a3343e..cfe12d3 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -638,6 +638,28 @@ static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
         return 0;
 }
 
+static int match_prepare_for_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
+        Manager *m = userdata;
+        int b, r;
+
+        assert(bus);
+        assert(bus);
+
+        r = sd_bus_message_read(message, "b", &b);
+        if (r < 0) {
+                log_debug("Failed to parse PrepareForSleep signal: %s", strerror(-r));
+                return 0;
+        }
+
+        if (b)
+                return 0;
+
+        log_debug("Coming back from suspend, verifying all RRs...");
+
+        manager_verify_all(m);
+        return 0;
+}
+
 int manager_connect_bus(Manager *m) {
         int r;
 
@@ -681,5 +703,16 @@ int manager_connect_bus(Manager *m) {
                 return r;
         }
 
+        r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
+                             "type='signal',"
+                             "sender='org.freedesktop.login1',"
+                             "interface='org.freedesktop.login1.Manager',"
+                             "member='PrepareForSleep',"
+                             "path='/org/freedesktop/login1'",
+                             match_prepare_for_sleep,
+                             m);
+        if (r < 0)
+                log_error("Failed to add match for PrepareForSleep: %s", strerror(-r));
+
         return 0;
 }
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index d96ddd2..5f250e7 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -625,3 +625,17 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
 
         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);
+        }
+}
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index 37fdafe..482df2f 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -74,3 +74,5 @@ void dns_zone_item_ready(DnsZoneItem *i);
 
 int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
 int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
+
+void dns_zone_verify_all(DnsZone *zone);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index a93f4a5..1d33c2a 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -554,6 +554,7 @@ Manager *manager_free(Manager *m) {
 
         manager_llmnr_stop(m);
 
+        sd_bus_slot_unref(m->prepare_for_sleep_slot);
         sd_event_source_unref(m->bus_retry_event_source);
         sd_bus_unref(m->bus);
 
@@ -1722,6 +1723,15 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
         return NULL;
 }
 
+void manager_verify_all(Manager *m) {
+        DnsScope *s;
+
+        assert(m);
+
+        LIST_FOREACH(scopes, s, m->dns_scopes)
+                dns_zone_verify_all(&s->zone);
+}
+
 static const char* const support_table[_SUPPORT_MAX] = {
         [SUPPORT_NO] = "no",
         [SUPPORT_YES] = "yes",
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index f960bc2..f8cb91c 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -108,6 +108,9 @@ struct Manager {
         /* Watch the system hostname */
         int hostname_fd;
         sd_event_source *hostname_event_source;
+
+        /* Watch for system suspends */
+        sd_bus_slot *prepare_for_sleep_slot;
 };
 
 /* Manager */
@@ -146,6 +149,8 @@ int manager_next_hostname(Manager *m);
 bool manager_our_packet(Manager *m, DnsPacket *p);
 DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
 
+void manager_verify_all(Manager *m);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
 #define EXTRA_CMSG_SPACE 1024

commit 82bd6dddc4a363a9c3c6f41eb46eb171a80dca27
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 16:32:55 2014 +0200

    resolved: destroy outstanding queries if the clients that initiated them die

diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 89a9300..6a3343e 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -319,6 +319,10 @@ static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, voi
         q->request_hostname = hostname;
         q->complete = bus_method_resolve_hostname_complete;
 
+        r = dns_query_bus_track(q, bus, message);
+        if (r < 0)
+                return r;
+
         r = dns_query_go(q);
         if (r < 0) {
                 dns_query_free(q);
@@ -457,6 +461,10 @@ static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void
         memcpy(&q->request_address, d, sz);
         q->complete = bus_method_resolve_address_complete;
 
+        r = dns_query_bus_track(q, bus, message);
+        if (r < 0)
+                return r;
+
         r = dns_query_go(q);
         if (r < 0) {
                 dns_query_free(q);
@@ -593,6 +601,10 @@ static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void
         q->request_hostname = name;
         q->complete = bus_method_resolve_record_complete;
 
+        r = dns_query_bus_track(q, bus, message);
+        if (r < 0)
+                return r;
+
         r = dns_query_go(q);
         if (r < 0) {
                 dns_query_free(q);
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index ae285ef..6d77c10 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -54,6 +54,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
         dns_answer_unref(q->answer);
 
         sd_bus_message_unref(q->request);
+        sd_bus_track_unref(q->bus_track);
 
         if (q->manager) {
                 LIST_REMOVE(queries, q->manager->dns_queries, q);
@@ -450,3 +451,33 @@ int dns_query_cname_redirect(DnsQuery *q, const char *name) {
 
         return 0;
 }
+
+static int on_bus_track(sd_bus_track *t, void *userdata) {
+        DnsQuery *q = userdata;
+
+        assert(t);
+        assert(q);
+
+        log_debug("Client of active query vanished, aborting query.");
+        dns_query_complete(q, DNS_TRANSACTION_ABORTED);
+        return 0;
+}
+
+int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) {
+        int r;
+
+        assert(q);
+        assert(m);
+
+        if (!q->bus_track) {
+                r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_track_add_sender(q->bus_track, m);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index adaf7b2..50fa3a2 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -63,6 +63,8 @@ struct DnsQuery {
 
         Set *transactions;
 
+        sd_bus_track *bus_track;
+
         LIST_FIELDS(DnsQuery, queries);
 };
 
@@ -74,4 +76,6 @@ void dns_query_ready(DnsQuery *q);
 
 int dns_query_cname_redirect(DnsQuery *q, const char *name);
 
+int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);

commit 818f766b12e025683cf4fed12b3da2a025bb0b31
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 16:32:17 2014 +0200

    resolved: properly pass aborted transaction result back to clients

diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index e158fdc..89a9300 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -51,11 +51,14 @@ static int reply_query_state(DnsQuery *q) {
         case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
                 return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
 
+        case DNS_TRANSACTION_INVALID_REPLY:
+                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
+
         case DNS_TRANSACTION_RESOURCES:
                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
 
-        case DNS_TRANSACTION_INVALID_REPLY:
-                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
+        case DNS_TRANSACTION_ABORTED:
+                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
 
         case DNS_TRANSACTION_FAILURE: {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
diff --git a/src/shared/bus-errors.h b/src/shared/bus-errors.h
index e4d0d43..504ab1f 100644
--- a/src/shared/bus-errors.h
+++ b/src/shared/bus-errors.h
@@ -65,4 +65,5 @@
 #define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR"
 #define BUS_ERROR_NO_RESOURCES "org.freedesktop.resolve1.NoResources"
 #define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop"
+#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted"
 #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."

commit a407657425a3e47fd2b559cd3bc800f791303f63
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 16:15:35 2014 +0200

    resolved: implement full LLMNR conflict detection logic

diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 696eb9d..733ef63 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -43,6 +43,8 @@ struct DnsCacheItem {
         usec_t until;
         DnsCacheItemType type;
         unsigned prioq_idx;
+        int owner_family;
+        union in_addr_union owner_address;
         LIST_FIELDS(DnsCacheItem, by_key);
 };
 
@@ -256,13 +258,20 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
 }
 
-static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t timestamp) {
+static int dns_cache_put_positive(
+                DnsCache *c,
+                DnsResourceRecord *rr,
+                usec_t timestamp,
+                int owner_family,
+                const union in_addr_union *owner_address) {
+
         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
         DnsCacheItem *existing;
         int r;
 
         assert(c);
         assert(rr);
+        assert(owner_address);
 
         /* New TTL is 0? Delete the entry... */
         if (rr->ttl <= 0) {
@@ -298,6 +307,8 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim
         i->rr = dns_resource_record_ref(rr);
         i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
         i->prioq_idx = PRIOQ_IDX_NULL;
+        i->owner_family = owner_family;
+        i->owner_address = *owner_address;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
@@ -307,12 +318,21 @@ static int dns_cache_put_positive(DnsCache *c, DnsResourceRecord *rr, usec_t tim
         return 0;
 }
 
-static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, usec_t timestamp, uint32_t soa_ttl) {
+static int dns_cache_put_negative(
+                DnsCache *c,
+                DnsResourceKey *key,
+                int rcode,
+                usec_t timestamp,
+                uint32_t soa_ttl,
+                int owner_family,
+                const union in_addr_union *owner_address) {
+
         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
         int r;
 
         assert(c);
         assert(key);
+        assert(owner_address);
 
         dns_cache_remove(c, key);
 
@@ -340,6 +360,8 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u
         i->key = dns_resource_key_ref(key);
         i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
         i->prioq_idx = PRIOQ_IDX_NULL;
+        i->owner_family = owner_family;
+        i->owner_address = *owner_address;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
@@ -349,7 +371,16 @@ static int dns_cache_put_negative(DnsCache *c, DnsResourceKey *key, int rcode, u
         return 0;
 }
 
-int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp) {
+int dns_cache_put(
+                DnsCache *c,
+                DnsQuestion *q,
+                int rcode,
+                DnsAnswer *answer,
+                unsigned max_rrs,
+                usec_t timestamp,
+                int owner_family,
+                const union in_addr_union *owner_address) {
+
         unsigned i;
         int r;
 
@@ -382,7 +413,7 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, uns
 
         /* Second, add in positive entries for all contained RRs */
         for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
-                r = dns_cache_put_positive(c, answer->rrs[i], timestamp);
+                r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
@@ -403,7 +434,7 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, uns
                 if (r == 0)
                         continue;
 
-                r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl));
+                r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
@@ -495,3 +526,39 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
 
         return n;
 }
+
+int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
+        DnsCacheItem *i, *first;
+        bool same_owner = true;
+
+        assert(cache);
+        assert(rr);
+
+        dns_cache_prune(cache);
+
+        /* See if there's a cache entry for the same key. If there
+         * isn't there's no conflict */
+        first = hashmap_get(cache->by_key, rr->key);
+        if (!first)
+                return 0;
+
+        /* See if the RR key is owned by the same owner, if so, there
+         * isn't a conflict either */
+        LIST_FOREACH(by_key, i, first) {
+                if (i->owner_family != owner_family ||
+                    !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
+                        same_owner = false;
+                        break;
+                }
+        }
+        if (same_owner)
+                return 0;
+
+        /* See if there's the exact same RR in the cache. If yes, then
+         * there's no conflict. */
+        if (dns_cache_get(cache, rr))
+                return 0;
+
+        /* There's a conflict */
+        return 1;
+}
diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h
index d88d1d0..e92280c 100644
--- a/src/resolve/resolved-dns-cache.h
+++ b/src/resolve/resolved-dns-cache.h
@@ -40,5 +40,7 @@ typedef struct DnsCache {
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
 
-int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp);
+int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
 int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **answer);
+
+int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 4f95038..0d276df 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -1358,6 +1358,9 @@ int dns_packet_extract(DnsPacket *p) {
         unsigned n, i;
         int r;
 
+        if (p->extracted)
+                return 0;
+
         saved_rindex = p->rindex;
         dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
 
@@ -1409,6 +1412,8 @@ int dns_packet_extract(DnsPacket *p) {
         p->answer = answer;
         answer = NULL;
 
+        p->extracted = true;
+
         r = 0;
 
 finish:
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 26a2e76..6a865a2 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -81,6 +81,8 @@ struct DnsPacket {
         union in_addr_union sender, destination;
         uint16_t sender_port, destination_port;
         uint32_t ttl;
+
+        bool extracted;
 };
 
 static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 40c326a..174249a 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -61,6 +61,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
 
 DnsScope* dns_scope_free(DnsScope *s) {
         DnsTransaction *t;
+        DnsResourceRecord *rr;
 
         if (!s)
                 return NULL;
@@ -81,6 +82,12 @@ DnsScope* dns_scope_free(DnsScope *s) {
                 dns_transaction_free(t);
         }
 
+        while ((rr = hashmap_steal_first(s->conflict_queue)))
+                dns_resource_record_unref(rr);
+
+        hashmap_free(s->conflict_queue);
+        sd_event_source_unref(s->conflict_event_source);
+
         dns_cache_flush(&s->cache);
         dns_zone_flush(&s->zone);
 
@@ -115,7 +122,7 @@ void dns_scope_next_dns_server(DnsScope *s) {
                 manager_next_dns_server(s->manager);
 }
 
-int dns_scope_send(DnsScope *s, DnsPacket *p) {
+int dns_scope_emit(DnsScope *s, DnsPacket *p) {
         union in_addr_union addr;
         int ifindex = 0, r;
         int family;
@@ -420,6 +427,7 @@ static int dns_scope_make_reply_packet(
         int r;
 
         assert(s);
+        assert(ret);
 
         if ((!q || q->n_keys <= 0)
             && (!answer || answer->n_rrs <= 0)
@@ -478,6 +486,20 @@ static int dns_scope_make_reply_packet(
         return 0;
 }
 
+static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
+        unsigned n;
+
+        assert(s);
+        assert(p);
+
+        if (p->question)
+                for (n = 0; n < p->question->n_keys; n++)
+                        dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
+        if (p->answer)
+                for (n = 0; n < p->answer->n_rrs; n++)
+                        dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key);
+}
+
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
@@ -509,7 +531,8 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
         }
 
         if (DNS_PACKET_C(p)) {
-                /* FIXME: Somebody notified us about a likely conflict */
+                /* Somebody notified us about a possible conflict */
+                dns_scope_verify_conflicts(s, p);
                 return;
         }
 
@@ -588,3 +611,174 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *questio
 
         return NULL;
 }
+
+static int dns_scope_make_conflict_packet(
+                DnsScope *s,
+                DnsResourceRecord *rr,
+                DnsPacket **ret) {
+
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        int r;
+
+        assert(s);
+        assert(rr);
+        assert(ret);
+
+        r = dns_packet_new(&p, s->protocol, 0);
+        if (r < 0)
+                return r;
+
+        DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+                                                              0 /* qr */,
+                                                              0 /* opcode */,
+                                                              1 /* conflict */,
+                                                              0 /* tc */,
+                                                              0 /* t */,
+                                                              0 /* (ra) */,
+                                                              0 /* (ad) */,
+                                                              0 /* (cd) */,
+                                                              0));
+        random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
+        DNS_PACKET_HEADER(p)->arcount = htobe16(1);
+
+        r = dns_packet_append_key(p, rr->key, NULL);
+        if (r < 0)
+                return r;
+
+        r = dns_packet_append_rr(p, rr, NULL);
+        if (r < 0)
+                return r;
+
+        *ret = p;
+        p = NULL;
+
+        return 0;
+}
+
+static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) {
+        DnsScope *scope = userdata;
+        int r;
+
+        assert(es);
+        assert(scope);
+
+        scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source);
+
+        for (;;) {
+                _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+                _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+
+                rr = hashmap_steal_first(scope->conflict_queue);
+                if (!rr)
+                        break;
+
+                r = dns_scope_make_conflict_packet(scope, rr, &p);
+                if (r < 0) {
+                        log_error("Failed to make conflict packet: %s", strerror(-r));
+                        return 0;
+                }
+
+                r = dns_scope_emit(scope, p);
+                if (r < 0)
+                        log_debug("Failed to send conflict packet: %s", strerror(-r));
+        }
+
+        return 0;
+}
+
+int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
+        usec_t jitter;
+        int r;
+
+        assert(scope);
+        assert(rr);
+
+        /* We don't send these queries immediately. Instead, we queue
+         * them, and send them after some jitter delay. */
+        r = hashmap_ensure_allocated(&scope->conflict_queue, dns_resource_key_hash_func, dns_resource_key_compare_func);
+        if (r < 0) {
+                log_oom();
+                return r;
+        }
+
+        /* We only place one RR per key in the conflict
+         * messages, not all of them. That should be enough to
+         * indicate where there might be a conflict */
+        r = hashmap_put(scope->conflict_queue, rr->key, rr);
+        if (r == -EEXIST || r == 0)
+                return 0;
+        if (r < 0) {
+                log_debug("Failed to queue conflicting RR: %s", strerror(-r));
+                return r;
+        }
+
+        dns_resource_record_ref(rr);
+
+        if (scope->conflict_event_source)
+                return 0;
+
+        random_bytes(&jitter, sizeof(jitter));
+        jitter %= LLMNR_JITTER_INTERVAL_USEC;
+
+        r = sd_event_add_time(scope->manager->event,
+                              &scope->conflict_event_source,
+                              clock_boottime_or_monotonic(),
+                              now(clock_boottime_or_monotonic()) + jitter,
+                              LLMNR_JITTER_INTERVAL_USEC,
+                              on_conflict_dispatch, scope);
+        if (r < 0) {
+                log_debug("Failed to add conflict dispatch event: %s", strerror(-r));
+                return r;
+        }
+
+        return 0;
+}
+
+void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
+        unsigned i;
+        int r;
+
+        assert(scope);
+        assert(p);
+
+        if (p->protocol != DNS_PROTOCOL_LLMNR)
+                return;
+
+        if (DNS_PACKET_RRCOUNT(p) <= 0)
+                return;
+
+        if (DNS_PACKET_C(p) != 0)
+                return;
+
+        if (DNS_PACKET_T(p) != 0)
+                return;
+
+        if (manager_our_packet(scope->manager, p))
+                return;
+
+        r = dns_packet_extract(p);
+        if (r < 0) {
+                log_debug("Failed to extract packet: %s", strerror(-r));
+                return;
+        }
+
+        log_debug("Checking for conflicts...");
+
+        for (i = 0; i < p->answer->n_rrs; i++) {
+
+                /* Check for conflicts against the local zone. If we
+                 * found one, we won't check any further */
+                r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]);
+                if (r != 0)
+                        continue;
+
+                /* Check for conflicts against the local cache. If so,
+                 * send out an advisory query, to inform everybody */
+                r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender);
+                if (r <= 0)
+                        continue;
+
+                dns_scope_notify_conflict(scope, p->answer->rrs[i]);
+        }
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index ae9469a..6ba5ef2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -55,6 +55,9 @@ struct DnsScope {
         DnsCache cache;
         DnsZone zone;
 
+        Hashmap *conflict_queue;
+        sd_event_source *conflict_event_source;
+
         RateLimit ratelimit;
 
         LIST_HEAD(DnsTransaction, transactions);
@@ -65,7 +68,7 @@ struct DnsScope {
 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family);
 DnsScope* dns_scope_free(DnsScope *s);
 
-int dns_scope_send(DnsScope *s, DnsPacket *p);
+int dns_scope_emit(DnsScope *s, DnsPacket *p);
 int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain);
@@ -80,3 +83,6 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b);
 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p);
 
 DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok);
+
+int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
+void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index a2e4f2c..e76940e 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -132,6 +132,15 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
                   t->scope->link ? t->scope->link->name : "*",
                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
 
+        /* RFC 4795, Section 4.1 says that the peer with the
+         * lexicographically smaller IP address loses */
+        if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) < 0) {
+                log_debug("Peer has lexicographically smaller IP address and thus lost in the conflict.");
+                return;
+        }
+
+        log_debug("We have the lexicographically smaller IP address and thus lost in the conflict.");
+
         t->block_gc++;
         SET_FOREACH(z, t->zone_items, i)
                 dns_zone_item_conflict(z);
@@ -196,6 +205,14 @@ static int on_stream_complete(DnsStream *s, int error) {
                 return 0;
         }
 
+        if (dns_packet_validate_reply(p) <= 0) {
+                log_debug("Invalid LLMNR TCP packet.");
+                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                return 0;
+        }
+
+        dns_scope_check_conflicts(t->scope, p);
+
         t->block_gc++;
         dns_transaction_process_reply(t, p);
         t->block_gc--;
@@ -370,7 +387,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         }
 
         /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
-        dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0);
+        dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
 
         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
@@ -507,7 +524,8 @@ int dns_transaction_go(DnsTransaction *t) {
                                 t->scope->manager->event,
                                 &t->timeout_event_source,
                                 clock_boottime_or_monotonic(),
-                                now(clock_boottime_or_monotonic()) + jitter, LLMNR_JITTER_INTERVAL_USEC,
+                                now(clock_boottime_or_monotonic()) + jitter,
+                                LLMNR_JITTER_INTERVAL_USEC,
                                 on_transaction_timeout, t);
                 if (r < 0)
                         return r;
@@ -542,7 +560,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 r = dns_transaction_open_tcp(t);
         } else {
                 /* Try via UDP, and if that fails due to large size try via TCP */
-                r = dns_scope_send(t->scope, t->sent);
+                r = dns_scope_emit(t->scope, t->sent);
                 if (r == -EMSGSIZE)
                         r = dns_transaction_open_tcp(t);
         }
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index ed47759..d96ddd2 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -493,6 +493,9 @@ 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));
 
@@ -507,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);
 
@@ -516,14 +521,107 @@ 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;
 
-                dns_resource_record_to_string(i->rr, &pretty);
-                log_debug("Record %s successfully probed.", strna(pretty));
+                /* 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 smaller IP address we continue
+                 * and defend it. */
+
+                if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+                        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) {
+                        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);
+        dns_zone_item_probe_stop(i);
+        i->state = DNS_ZONE_ITEM_ESTABLISHED;
+}
+
+static int dns_zone_item_verify(DnsZoneItem *i) {
+        int r;
+
+        assert(i);
+
+        if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
+                return 0;
+
+        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;
-        } else
-                dns_zone_item_conflict(i);
+                return r;
+        }
+
+        return 0;
+}
+
+int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
+        DnsZoneItem *i, *first;
+        int c;
+
+        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;
+
+        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;
 }
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index bf93ab4..37fdafe 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -71,3 +71,6 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **
 
 void dns_zone_item_conflict(DnsZoneItem *i);
 void dns_zone_item_ready(DnsZoneItem *i);
+
+int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
+int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 1288395..a93f4a5 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -1219,38 +1219,34 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         DnsTransaction *t = NULL;
         Manager *m = userdata;
+        DnsScope *scope;
         int r;
 
         r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
         if (r <= 0)
                 return r;
 
+        scope = manager_find_scope(m, p);
+        if (!scope) {
+                log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
+                return 0;
+        }
+
         if (dns_packet_validate_reply(p) > 0) {
                 log_debug("Got reply packet for id %u", DNS_PACKET_ID(p));
 
-                t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
-                if (!t)
-                        return 0;
-
-                dns_transaction_process_reply(t, p);
+                dns_scope_check_conflicts(scope, p);
 
-        } else if (dns_packet_validate_query(p) > 0) {
-                Link *l;
-
-                l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
-                if (l) {
-                        DnsScope *scope = NULL;
+                t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
+                if (t)
+                        dns_transaction_process_reply(t, p);
 
-                        if (p->family == AF_INET)
-                                scope = l->llmnr_ipv4_scope;
-                        else if (p->family == AF_INET6)
-                                scope = l->llmnr_ipv6_scope;
+        } else if (dns_packet_validate_query(p) > 0)  {
+                log_debug("Got query packet for id %u", DNS_PACKET_ID(p));
 
-                        if (scope)
-                                dns_scope_process_query(scope, NULL, p);
-                }
+                dns_scope_process_query(scope, NULL, p);
         } else
-                log_debug("Invalid LLMNR packet.");
+                log_debug("Invalid LLMNR UDP packet.");
 
         return 0;
 }
@@ -1413,29 +1409,26 @@ fail:
 }
 
 static int on_llmnr_stream_packet(DnsStream *s) {
-        assert(s);
+        DnsScope *scope;
 
-        if (dns_packet_validate_query(s->read_packet) > 0) {
-                Link *l;
+        assert(s);
 
-                l = hashmap_get(s->manager->links, INT_TO_PTR(s->read_packet->ifindex));
-                if (l) {
-                        DnsScope *scope = NULL;
+        scope = manager_find_scope(s->manager, s->read_packet);
+        if (!scope) {
+                log_warning("Got LLMNR TCP packet on unknown scope. Ignroing.");
+                return 0;
+        }
 
-                        if (s->read_packet->family == AF_INET)
-                                scope = l->llmnr_ipv4_scope;
-                        else if (s->read_packet->family == AF_INET6)
-                                scope = l->llmnr_ipv6_scope;
+        if (dns_packet_validate_query(s->read_packet) > 0) {
+                log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet));
 
-                        if (scope) {
-                                dns_scope_process_query(scope, s, s->read_packet);
+                dns_scope_process_query(scope, s, s->read_packet);
 
-                                /* If no reply packet was set, we free the stream */
-                                if (s->write_packet)
-                                        return 0;
-                        }
-                }
-        }
+                /* If no reply packet was set, we free the stream */
+                if (s->write_packet)
+                        return 0;
+        } else
+                log_debug("Invalid LLMNR TCP packet.");
 
         dns_stream_free(s);
         return 0;
@@ -1702,13 +1695,33 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad
         return NULL;
 }
 
-int manager_our_packet(Manager *m, DnsPacket *p) {
+bool manager_our_packet(Manager *m, DnsPacket *p) {
         assert(m);
         assert(p);
 
         return !!manager_find_link_address(m, p->family, &p->sender);
 }
 
+DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
+        Link *l;
+
+        assert(m);
+        assert(p);
+
+        l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
+        if (!l)
+                return NULL;
+
+        if (p->protocol == DNS_PROTOCOL_LLMNR) {
+                if (p->family == AF_INET)
+                        return l->llmnr_ipv4_scope;
+                else if (p->family == AF_INET6)
+                        return l->llmnr_ipv6_scope;
+        }
+
+        return NULL;
+}
+
 static const char* const support_table[_SUPPORT_MAX] = {
         [SUPPORT_NO] = "no",
         [SUPPORT_YES] = "yes",
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 9d824e1..f960bc2 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -143,7 +143,8 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad
 void manager_refresh_rrs(Manager *m);
 int manager_next_hostname(Manager *m);
 
-int manager_our_packet(Manager *m, DnsPacket *p);
+bool manager_our_packet(Manager *m, DnsPacket *p);
+DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 

commit 3ef77d0476046a660c1b4704140797c447e6ce3a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Aug 6 16:14:53 2014 +0200

    resolved: properly check return value of dns_resource_record_equal()

diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 40fb6c3..696eb9d 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -224,7 +224,7 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
         assert(rr);
 
         LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
-                if (i->rr && dns_resource_record_equal(i->rr, rr))
+                if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
                         return i;
 
         return NULL;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index b97fd17..4f95038 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -1029,6 +1029,8 @@ static bool loc_size_ok(uint8_t size) {
 }
 
 static int dnskey_parse_flags(DnsResourceRecord *rr, uint16_t flags) {
+        assert(rr);
+
         if (flags & ~(DNSKEY_FLAG_SEP | DNSKEY_FLAG_ZONE_KEY))
                 return -EBADMSG;
 
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 72321d0..ed47759 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -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;



More information about the systemd-commits mailing list