|
|
b7dd4d |
From b9844d3dd9d7fdf81d475b81d06a6e9ec821f91d Mon Sep 17 00:00:00 2001
|
|
|
b7dd4d |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
b7dd4d |
Date: Mon, 9 Nov 2020 22:22:56 +0100
|
|
|
b7dd4d |
Subject: [PATCH] resolved: let's preferably route reverse lookups for local
|
|
|
b7dd4d |
subnets to matching interfaces
|
|
|
b7dd4d |
|
|
|
b7dd4d |
Let's preferably route traffic for reverse lookups to LLMNR/mDNS/DNS on
|
|
|
b7dd4d |
the matching interface if the IP address is in the local subnet. Also,
|
|
|
b7dd4d |
if looking up an IP address of our own host, let's avoid doing
|
|
|
b7dd4d |
LLMNR/mDNS at all.
|
|
|
b7dd4d |
|
|
|
b7dd4d |
This is useful if "~." is a routing domain to DNS, as it means, local
|
|
|
b7dd4d |
reverse lookups still go to LLMNR/mDNS, too.
|
|
|
b7dd4d |
|
|
|
b7dd4d |
(cherry picked from commit 13eb76ef06f5d50bbeb58df1744057e41ef2647e)
|
|
|
b7dd4d |
|
|
|
b7dd4d |
Resolves #1739689
|
|
|
b7dd4d |
---
|
|
|
b7dd4d |
src/resolve/resolved-dns-scope.c | 85 +++++++++++++++++++++++++++++++-
|
|
|
b7dd4d |
src/resolve/resolved-link.c | 12 +++--
|
|
|
b7dd4d |
src/resolve/resolved-link.h | 1 +
|
|
|
b7dd4d |
3 files changed, 92 insertions(+), 6 deletions(-)
|
|
|
b7dd4d |
|
|
|
b7dd4d |
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
|
|
|
b7dd4d |
index 38ea7fea0a..8b65813428 100644
|
|
|
b7dd4d |
--- a/src/resolve/resolved-dns-scope.c
|
|
|
b7dd4d |
+++ b/src/resolve/resolved-dns-scope.c
|
|
|
b7dd4d |
@@ -417,6 +417,65 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
|
|
|
b7dd4d |
return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address);
|
|
|
b7dd4d |
}
|
|
|
b7dd4d |
|
|
|
b7dd4d |
+static DnsScopeMatch match_subnet_reverse_lookups(
|
|
|
b7dd4d |
+ DnsScope *s,
|
|
|
b7dd4d |
+ const char *domain,
|
|
|
b7dd4d |
+ bool exclude_own) {
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ union in_addr_union ia;
|
|
|
b7dd4d |
+ LinkAddress *a;
|
|
|
b7dd4d |
+ int f, r;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ assert(s);
|
|
|
b7dd4d |
+ assert(domain);
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ /* Checks whether the specified domain is a reverse address domain (i.e. in the .in-addr.arpa or
|
|
|
b7dd4d |
+ * .ip6.arpa area), and if so, whether the address matches any of the local subnets of the link the
|
|
|
b7dd4d |
+ * scope is associated with. If so, our scope should consider itself relevant for any lookup in the
|
|
|
b7dd4d |
+ * domain, since it apparently refers to hosts on this link's subnet.
|
|
|
b7dd4d |
+ *
|
|
|
b7dd4d |
+ * If 'exclude_own' is true this will return DNS_SCOPE_NO for any IP addresses assigned locally. This
|
|
|
b7dd4d |
+ * is useful for LLMNR/mDNS as we never want to look up our own hostname on LLMNR/mDNS but always use
|
|
|
b7dd4d |
+ * the locally synthesized one. */
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ if (!s->link)
|
|
|
b7dd4d |
+ return _DNS_SCOPE_INVALID; /* No link, hence no local addresses to check */
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ r = dns_name_address(domain, &f, &ia);
|
|
|
b7dd4d |
+ if (r < 0)
|
|
|
b7dd4d |
+ log_debug_errno(r, "Failed to determine whether '%s' is an address domain: %m", domain);
|
|
|
b7dd4d |
+ if (r <= 0)
|
|
|
b7dd4d |
+ return _DNS_SCOPE_INVALID;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ if (s->family != AF_UNSPEC && f != s->family)
|
|
|
b7dd4d |
+ return _DNS_SCOPE_INVALID; /* Don't look for IPv4 addresses on LLMNR/mDNS over IPv6 and vice versa */
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ LIST_FOREACH(addresses, a, s->link->addresses) {
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ if (a->family != f)
|
|
|
b7dd4d |
+ continue;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ /* Equals our own address? nah, let's not use this scope. The local synthesizer will pick it up for us. */
|
|
|
b7dd4d |
+ if (exclude_own &&
|
|
|
b7dd4d |
+ in_addr_equal(f, &a->in_addr, &ia) > 0)
|
|
|
b7dd4d |
+ return DNS_SCOPE_NO;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ if (a->prefixlen == UCHAR_MAX) /* don't know subnet mask */
|
|
|
b7dd4d |
+ continue;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ /* Check if the address is in the local subnet */
|
|
|
b7dd4d |
+ r = in_addr_prefix_covers(f, &a->in_addr, a->prefixlen, &ia);
|
|
|
b7dd4d |
+ if (r < 0)
|
|
|
b7dd4d |
+ log_debug_errno(r, "Failed to determine whether link address covers lookup address '%s': %m", domain);
|
|
|
b7dd4d |
+ if (r > 0)
|
|
|
b7dd4d |
+ /* Note that we only claim zero labels match. This is so that this is at the same
|
|
|
b7dd4d |
+ * priority a DNS scope with "." as routing domain is. */
|
|
|
b7dd4d |
+ return DNS_SCOPE_YES + 0;
|
|
|
b7dd4d |
+ }
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ return _DNS_SCOPE_INVALID;
|
|
|
b7dd4d |
+}
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
|
|
|
b7dd4d |
DnsSearchDomain *d;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
@@ -455,6 +514,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|
|
b7dd4d |
|
|
|
b7dd4d |
case DNS_PROTOCOL_DNS: {
|
|
|
b7dd4d |
DnsServer *dns_server;
|
|
|
b7dd4d |
+ DnsScopeMatch m;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
/* Never route things to scopes that lack DNS servers */
|
|
|
b7dd4d |
dns_server = dns_scope_get_dns_server(s);
|
|
|
b7dd4d |
@@ -485,10 +545,23 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|
|
b7dd4d |
dns_name_endswith(domain, "local") == 0)
|
|
|
b7dd4d |
return DNS_SCOPE_MAYBE;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
+ /* If the IP address to look up matches the local subnet, then implicity synthesizes
|
|
|
b7dd4d |
+ * DNS_SCOPE_YES_BASE + 0 on this interface, i.e. preferably resolve IP addresses via the DNS
|
|
|
b7dd4d |
+ * server belonging to this interface. */
|
|
|
b7dd4d |
+ m = match_subnet_reverse_lookups(s, domain, false);
|
|
|
b7dd4d |
+ if (m >= 0)
|
|
|
b7dd4d |
+ return m;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
return DNS_SCOPE_NO;
|
|
|
b7dd4d |
}
|
|
|
b7dd4d |
|
|
|
b7dd4d |
- case DNS_PROTOCOL_MDNS:
|
|
|
b7dd4d |
+ case DNS_PROTOCOL_MDNS: {
|
|
|
b7dd4d |
+ DnsScopeMatch m;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ m = match_subnet_reverse_lookups(s, domain, true);
|
|
|
b7dd4d |
+ if (m >= 0)
|
|
|
b7dd4d |
+ return m;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
|
|
|
b7dd4d |
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
|
|
|
b7dd4d |
(dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */
|
|
|
b7dd4d |
@@ -497,8 +570,15 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|
|
b7dd4d |
return DNS_SCOPE_MAYBE;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
return DNS_SCOPE_NO;
|
|
|
b7dd4d |
+ }
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ case DNS_PROTOCOL_LLMNR: {
|
|
|
b7dd4d |
+ DnsScopeMatch m;
|
|
|
b7dd4d |
+
|
|
|
b7dd4d |
+ m = match_subnet_reverse_lookups(s, domain, true);
|
|
|
b7dd4d |
+ if (m >= 0)
|
|
|
b7dd4d |
+ return m;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
- case DNS_PROTOCOL_LLMNR:
|
|
|
b7dd4d |
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
|
|
|
b7dd4d |
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
|
|
|
b7dd4d |
(dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
|
|
|
b7dd4d |
@@ -507,6 +587,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|
|
b7dd4d |
return DNS_SCOPE_MAYBE;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
return DNS_SCOPE_NO;
|
|
|
b7dd4d |
+ }
|
|
|
b7dd4d |
|
|
|
b7dd4d |
default:
|
|
|
b7dd4d |
assert_not_reached("Unknown scope protocol");
|
|
|
b7dd4d |
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
|
|
|
b7dd4d |
index ff2be12415..c42fe5b5f4 100644
|
|
|
b7dd4d |
--- a/src/resolve/resolved-link.c
|
|
|
b7dd4d |
+++ b/src/resolve/resolved-link.c
|
|
|
b7dd4d |
@@ -776,10 +776,13 @@ int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr
|
|
|
b7dd4d |
if (!a)
|
|
|
b7dd4d |
return -ENOMEM;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
- a->family = family;
|
|
|
b7dd4d |
- a->in_addr = *in_addr;
|
|
|
b7dd4d |
+ *a = (LinkAddress) {
|
|
|
b7dd4d |
+ .family = family,
|
|
|
b7dd4d |
+ .in_addr = *in_addr,
|
|
|
b7dd4d |
+ .link = l,
|
|
|
b7dd4d |
+ .prefixlen = UCHAR_MAX,
|
|
|
b7dd4d |
+ };
|
|
|
b7dd4d |
|
|
|
b7dd4d |
- a->link = l;
|
|
|
b7dd4d |
LIST_PREPEND(addresses, l->addresses, a);
|
|
|
b7dd4d |
l->n_addresses++;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
@@ -1077,7 +1080,8 @@ int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
|
|
|
b7dd4d |
if (r < 0)
|
|
|
b7dd4d |
return r;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
- sd_rtnl_message_addr_get_scope(m, &a->scope);
|
|
|
b7dd4d |
+ (void) sd_rtnl_message_addr_get_prefixlen(m, &a->prefixlen);
|
|
|
b7dd4d |
+ (void) sd_rtnl_message_addr_get_scope(m, &a->scope);
|
|
|
b7dd4d |
|
|
|
b7dd4d |
link_allocate_scopes(a->link);
|
|
|
b7dd4d |
link_add_rrs(a->link, false);
|
|
|
b7dd4d |
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
|
|
|
b7dd4d |
index 063d3f35c3..8d52b10950 100644
|
|
|
b7dd4d |
--- a/src/resolve/resolved-link.h
|
|
|
b7dd4d |
+++ b/src/resolve/resolved-link.h
|
|
|
b7dd4d |
@@ -24,6 +24,7 @@ struct LinkAddress {
|
|
|
b7dd4d |
|
|
|
b7dd4d |
int family;
|
|
|
b7dd4d |
union in_addr_union in_addr;
|
|
|
b7dd4d |
+ unsigned char prefixlen;
|
|
|
b7dd4d |
|
|
|
b7dd4d |
unsigned char flags, scope;
|
|
|
b7dd4d |
|