[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