From 2a1037af15dfcfdaea5888fee310c357b8be199d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Aug 2015 13:17:05 +0200 Subject: resolved: locally synthesize replies for "localhost" Let's make sure that clients querying resolved via the bus for A, AAAA or PTR records for "localhost" get a synthesized, local reply, so that we do not hit the network. This makes part of nss-myhostname redundant, if used in conjunction. However, given that nss-resolve shall be optional we need to keep this code in both places for now. --- src/resolve/resolved-dns-query.c | 166 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 418d9721ef..3ada442006 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -19,6 +19,8 @@ along with systemd; If not, see . ***/ +#include "hostname-util.h" +#include "dns-domain.h" #include "resolved-dns-query.h" @@ -212,6 +214,156 @@ static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { return 0; } +static int SYNTHESIZE_IFINDEX(int ifindex) { + + /* When the caller asked for resolving on a specific interface, + * we synthesize the answer for that interface. However, if + * nothing specific was claimed, we synthesize the answer for + * localhost. */ + + if (ifindex > 0) + return ifindex; + + return LOOPBACK_IFINDEX; +} + +static int SYNTHESIZE_FAMILY(uint64_t flags) { + + /* Picks an address family depending on set flags. This is + * purely for synthesized answers, where the family we return + * for the reply should match what was requested in the + * question, even though we are synthesizing the answer + * here. */ + + if (!(flags & SD_RESOLVED_DNS)) { + if (flags & SD_RESOLVED_LLMNR_IPV4) + return AF_INET; + if (flags & SD_RESOLVED_LLMNR_IPV6) + return AF_INET6; + } + + return AF_UNSPEC; +} + +static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) { + + /* Similar as SYNTHESIZE_FAMILY() but does this for the + * protocol. If resolving via DNS was requested, we claim it + * was DNS. Similar, if nothing specific was + * requested. However, if only resolving via LLMNR was + * requested we return that. */ + + if (flags & SD_RESOLVED_DNS) + return DNS_PROTOCOL_DNS; + if (flags & SD_RESOLVED_LLMNR) + return DNS_PROTOCOL_LLMNR; + + return DNS_PROTOCOL_DNS; +} + +static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + unsigned i; + int r; + + assert(q); + assert(state); + + /* Tries to synthesize localhost RR replies where appropriate */ + + if (!IN_SET(*state, + DNS_TRANSACTION_FAILURE, + DNS_TRANSACTION_NO_SERVERS, + DNS_TRANSACTION_TIMEOUT, + DNS_TRANSACTION_ATTEMPTS_MAX_REACHED)) + return; + + for (i = 0; i < q->question->n_keys; i++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + const char *name; + + if (q->question->keys[i]->class != DNS_CLASS_IN && + q->question->keys[i]->class != DNS_CLASS_ANY) + continue; + + name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]); + + if (is_localhost(name)) { + + switch (q->question->keys[i]->type) { + + case DNS_TYPE_A: + case DNS_TYPE_ANY: + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); + if (!rr) { + log_oom(); + return; + } + + rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); + break; + + case DNS_TYPE_AAAA: + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); + if (!rr) { + log_oom(); + return; + } + + rr->aaaa.in6_addr = in6addr_loopback; + break; + } + + } else if (IN_SET(q->question->keys[i]->type, DNS_TYPE_PTR, DNS_TYPE_ANY) && + (dns_name_endswith(name, "127.in-addr.arpa") > 0 || + dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, name); + if (!rr) { + log_oom(); + return; + } + + rr->ptr.name = strdup("localhost"); + if (!rr->ptr.name) { + log_oom(); + return; + } + } + + if (!rr) + continue; + + if (!answer) { + answer = dns_answer_new(q->question->n_keys); + if (!answer) { + log_oom(); + return; + } + } + + r = dns_answer_add(answer, rr); + if (r < 0) { + log_error_errno(r, "Failed to add synthetic RR to answer: %m"); + return; + } + } + + if (!answer) + return; + + dns_answer_unref(q->answer); + q->answer = answer; + answer = NULL; + + q->answer_ifindex = SYNTHESIZE_IFINDEX(q->ifindex); + q->answer_family = SYNTHESIZE_FAMILY(q->flags); + q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); + q->answer_rcode = DNS_RCODE_SUCCESS; + + *state = DNS_TRANSACTION_SUCCESS; +} + int dns_query_go(DnsQuery *q) { DnsScopeMatch found = DNS_SCOPE_NO; DnsScope *s, *first = NULL; @@ -253,8 +405,17 @@ int dns_query_go(DnsQuery *q) { } } - if (found == DNS_SCOPE_NO) + if (found == DNS_SCOPE_NO) { + DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; + + dns_query_synthesize_reply(q, &state); + if (state != DNS_TRANSACTION_NO_SERVERS) { + dns_query_complete(q, state); + return 1; + } + return -ESRCH; + } r = dns_query_add_transaction_split(q, first); if (r < 0) @@ -428,6 +589,9 @@ void dns_query_ready(DnsQuery *q) { q->answer_family = scope ? scope->family : AF_UNSPEC; } + /* Try to synthesize a reply if we couldn't resolve something. */ + dns_query_synthesize_reply(q, &state); + dns_query_complete(q, state); } -- cgit v1.2.3-54-g00ecf From 9b644bf921ca3b1f3967a794932c8e56636908db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 14 Aug 2015 13:21:28 +0200 Subject: resolved: never allow routing of "localhost" queries to DNS or LLMNR We should never allow leaking of "localhost" queries onto the network, even if there's an explicit domain rotue set for this. --- src/resolve/resolved-dns-scope.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b8414da87e..57d9071dfc 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -325,10 +325,6 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0) return DNS_SCOPE_NO; - STRV_FOREACH(i, s->domains) - if (dns_name_endswith(domain, *i) > 0) - return DNS_SCOPE_YES; - if (dns_name_root(domain) != 0) return DNS_SCOPE_NO; @@ -340,6 +336,10 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; + STRV_FOREACH(i, s->domains) + if (dns_name_endswith(domain, *i) > 0) + return DNS_SCOPE_YES; + if (s->protocol == DNS_PROTOCOL_DNS) { if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && -- cgit v1.2.3-54-g00ecf