From b43d96b0764e63088429f746cd9e515f55286460 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 8 Dec 2015 18:29:52 +0100 Subject: [PATCH 1/6] resolved: don't send .local requests to DNS servers DNS names ending with .local are specific to mDNS, so don't use them on DNS scopes. --- src/resolve/resolved-dns-scope.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index eae903526b8..91e23531f52 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -433,7 +433,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 && dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 && dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 && - dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0) + dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0 && + /* If networks use .local in their private setups, they are supposed to also add .local to their search + * domains, which we already checked above. Otherwise, we consider .local specific to mDNS and won't + * send such queries ordinary DNS servers. */ + dns_name_endswith(domain, "local") == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; From ee8d930568456a6fd3f4ea0117f900b957044209 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 9 Dec 2015 10:24:27 +0100 Subject: [PATCH 2/6] resolved: llmnr, mdns: simplify error handling sd_event_add_io() returns the error directly and does not mess with errno. --- src/resolve/resolved-llmnr.c | 4 +--- src/resolve/resolved-mdns.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 6a7ff9d2458..ed754c38996 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -461,10 +461,8 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) { } r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m); - if (r < 0) { - r = -errno; + if (r < 0) goto fail; - } return m->llmnr_ipv6_tcp_fd; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 096a4b1fe55..abe63d58c1d 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -275,10 +275,8 @@ int manager_mdns_ipv6_fd(Manager *m) { } r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m); - if (r < 0) { - r = -errno; + if (r < 0) goto fail; - } return m->mdns_ipv6_fd; From dbfbb6e776d613cb9be76d13de076d08450c9d29 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 9 Dec 2015 11:55:54 +0100 Subject: [PATCH 3/6] resolved: add dns_packet_set_flags() We need to support the TC bit in queries in case known answers exceed the maximum packet size. Factor out the flags compilation to dns_packet_set_flags() and make it externally available. --- src/resolve/resolved-dns-packet.c | 63 ++++++++++++++++++++----------- src/resolve/resolved-dns-packet.h | 2 + 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 9bd08eeec25..e5de5a1cd6a 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -65,40 +65,44 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { return 0; } -int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) { - DnsPacket *p; +void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) { + DnsPacketHeader *h; - int r; - assert(ret); - - r = dns_packet_new(&p, protocol, mtu); - if (r < 0) - return r; + assert(p); h = DNS_PACKET_HEADER(p); - if (protocol == DNS_PROTOCOL_LLMNR) + switch(p->protocol) { + case DNS_PROTOCOL_LLMNR: + assert(!truncated); + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, 0 /* opcode */, 0 /* c */, - 0 /* tc */, + 0/* tc */, 0 /* t */, 0 /* ra */, 0 /* ad */, 0 /* cd */, 0 /* rcode */)); - else if (protocol == DNS_PROTOCOL_MDNS) - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, - 0 /* opcode */, - 0 /* aa */, - 0 /* tc */, - 0 /* rd (ask for recursion) */, - 0 /* ra */, - 0 /* ad */, - 0 /* cd */, - 0 /* rcode */)); - else + break; + + case DNS_PROTOCOL_MDNS: + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, + 0 /* opcode */, + 0 /* aa */, + truncated /* tc */, + 0 /* rd (ask for recursion) */, + 0 /* ra */, + 0 /* ad */, + 0 /* cd */, + 0 /* rcode */)); + break; + + default: + assert(!truncated); + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, 0 /* opcode */, 0 /* aa */, @@ -108,6 +112,23 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool 0 /* ad */, dnssec_checking_disabled /* cd */, 0 /* rcode */)); + } +} + +int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) { + DnsPacket *p; + int r; + + assert(ret); + + r = dns_packet_new(&p, protocol, mtu); + if (r < 0) + return r; + + /* Always set the TC bit to 0 initially. + * If there are multiple packets later, we'll update the bit shortly before sending. + */ + dns_packet_set_flags(p, dnssec_checking_disabled, false); *ret = p; return 0; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 48b3572cb4a..405838e60d5 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -146,6 +146,8 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu); int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled); +void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated); + DnsPacket *dns_packet_ref(DnsPacket *p); DnsPacket *dns_packet_unref(DnsPacket *p); From 9c491563837983385bf9fa244590e76e142f4fa3 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 9 Dec 2015 12:01:08 +0100 Subject: [PATCH 4/6] resolved: add support for linked packets For mDNS, we need to support the TC bit in case the list of known answers exceed the maximum packet size. For this, add a 'more' pointer to DnsPacket for an additional packet. When a packet is unref'ed, the ->more packet is also unrefed, so it sufficient to only keep track of the 1st packet in a chain. --- src/resolve/resolved-dns-packet.c | 3 +++ src/resolve/resolved-dns-packet.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index e5de5a1cd6a..f753b3522f0 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -170,6 +170,9 @@ DnsPacket *dns_packet_unref(DnsPacket *p) { assert(p->n_ref > 0); + if (p->more) + dns_packet_unref(p->more); + if (p->n_ref == 1) dns_packet_free(p); else diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 405838e60d5..3d84cb622ba 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -88,6 +88,9 @@ struct DnsPacket { uint16_t sender_port, destination_port; uint32_t ttl; + /* For support of truncated packets */ + DnsPacket *more; + bool on_stack:1; bool extracted:1; bool refuse_compression:1; From 80a62095dc5af36d9f46de693f3a84835bc28e96 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 9 Dec 2015 12:05:38 +0100 Subject: [PATCH 5/6] resolved: handle linked packet in dns_scope_emit() In dns_scope_emit(), walk the list of additional packets and emit all of them. Set the TC bit in all but the last of them. This is specific to mDNS, so an assertion is triggered if used with other protocols. --- src/resolve/resolved-dns-scope.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 91e23531f52..11c3294ff3e 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -161,7 +161,7 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) { s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC); } -int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { +static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; @@ -278,6 +278,31 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { return 1; } +int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { + int r; + + assert(s); + assert(p); + assert(p->protocol == s->protocol); + assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0)); + + do { + /* If there are multiple linked packets, set the TC bit in all but the last of them */ + if (p->more) { + assert(p->protocol == DNS_PROTOCOL_MDNS); + dns_packet_set_truncated_flag(p, true); + } + + r = dns_scope_emit_one(s, fd, server, p); + if (r < 0) + return r; + + p = p->more; + } while(p); + + return 0; +} + static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { DnsServer *srv = NULL; _cleanup_close_ int fd = -1; From 261f3673c197ff7e52722c212ae63baf853b6896 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 9 Dec 2015 13:09:35 +0100 Subject: [PATCH 6/6] resolved: add more linked packets for overlong known answers For mDNS, if we're unable to stuff all known answers into the given packet, allocate a new one, push the RR into that one and link it to the current one. --- src/resolve/resolved-dns-cache.c | 18 ++++++++++++++++++ src/resolve/resolved-dns-scope.c | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 6124ff659c9..1774ae6cb8a 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -738,6 +738,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { int r; assert(cache); + assert(p); HASHMAP_FOREACH(i, cache->by_key, iterator) { DnsCacheItem *j; @@ -752,6 +753,23 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { continue; r = dns_packet_append_rr(p, j->rr, NULL, NULL); + if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) { + /* For mDNS, if we're unable to stuff all known answers into the given packet, + * allocate a new one, push the RR into that one and link it to the current one. + */ + + DNS_PACKET_HEADER(p)->ancount = htobe16(ancount); + ancount = 0; + + r = dns_packet_new_query(&p->more, p->protocol, 0, true); + if (r < 0) + return r; + + /* continue with new packet */ + p = p->more; + r = dns_packet_append_rr(p, j->rr, NULL, NULL); + } + if (r < 0) return r; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 11c3294ff3e..4d83ac597c4 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -290,7 +290,7 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { /* If there are multiple linked packets, set the TC bit in all but the last of them */ if (p->more) { assert(p->protocol == DNS_PROTOCOL_MDNS); - dns_packet_set_truncated_flag(p, true); + dns_packet_set_flags(p, true, true); } r = dns_scope_emit_one(s, fd, server, p);