diff options
Diffstat (limited to 'src/resolve')
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 64 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-query.c | 166 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 8 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 3 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 3 |
6 files changed, 231 insertions, 15 deletions
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9ffaf4b19f..81ea1cafcb 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -95,14 +95,19 @@ void dns_cache_flush(DnsCache *c) { c->by_expiry = prioq_free(c->by_expiry); } -static void dns_cache_remove(DnsCache *c, DnsResourceKey *key) { +static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) { DnsCacheItem *i; + bool exist = false; assert(c); assert(key); - while ((i = hashmap_get(c->by_key, key))) + while ((i = hashmap_get(c->by_key, key))) { dns_cache_item_remove_and_free(c, i); + exist = true; + } + + return exist; } static void dns_cache_make_space(DnsCache *c, unsigned add) { @@ -263,6 +268,7 @@ static int dns_cache_put_positive( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; + _cleanup_free_ char *key_str = NULL; DnsCacheItem *existing; int r; @@ -272,7 +278,14 @@ static int dns_cache_put_positive( /* New TTL is 0? Delete the entry... */ if (rr->ttl <= 0) { - dns_cache_remove(c, rr->key); + if (dns_cache_remove(c, rr->key)) { + r = dns_resource_key_to_string(rr->key, &key_str); + if (r < 0) + return r; + + log_debug("Removed zero TTL entry from cache: %s", key_str); + } + return 0; } @@ -311,6 +324,12 @@ static int dns_cache_put_positive( if (r < 0) return r; + r = dns_resource_key_to_string(i->key, &key_str); + if (r < 0) + return r; + + log_debug("Added cache entry for %s", key_str); + i = NULL; return 0; } @@ -325,6 +344,7 @@ static int dns_cache_put_negative( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; + _cleanup_free_ char *key_str = NULL; int r; assert(c); @@ -337,8 +357,15 @@ static int dns_cache_put_negative( return 0; if (key->type == DNS_TYPE_ANY) return 0; - if (soa_ttl <= 0) + if (soa_ttl <= 0) { + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; + + log_debug("Ignored negative cache entry with zero SOA TTL: %s", key_str); + return 0; + } if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) return 0; @@ -364,6 +391,12 @@ static int dns_cache_put_negative( if (r < 0) return r; + r = dns_resource_key_to_string(i->key, &key_str); + if (r < 0) + return r; + + log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str); + i = NULL; return 0; } @@ -468,11 +501,19 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { } for (i = 0; i < q->n_keys; i++) { + _cleanup_free_ char *key_str = NULL; DnsCacheItem *j; if (q->keys[i]->type == DNS_TYPE_ANY || q->keys[i]->class == DNS_CLASS_ANY) { /* If we have ANY lookups we simply refresh */ + + r = dns_resource_key_to_string(q->keys[i], &key_str); + if (r < 0) + return r; + + log_debug("Ignoring cache for ANY lookup: %s", key_str); + *ret = NULL; *rcode = 0; return 0; @@ -481,9 +522,24 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { j = hashmap_get(c->by_key, q->keys[i]); if (!j) { /* If one question cannot be answered we need to refresh */ + + r = dns_resource_key_to_string(q->keys[i], &key_str); + if (r < 0) + return r; + + log_debug("Cache miss for %s", key_str); + *ret = NULL; *rcode = 0; return 0; + } else { + r = dns_resource_key_to_string(j->key, &key_str); + if (r < 0) + return r; + + log_debug("%s cache hit for %s", + j->type == DNS_CACHE_POSITIVE ? "Positive" : + (j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN"), key_str); } LIST_FOREACH(by_key, j, j) { diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 39951a362c..35ad899544 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1712,7 +1712,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); + r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); if (r < 0) goto fail; 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 <http://www.gnu.org/licenses/>. ***/ +#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); } 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 && diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 53779f3372..2d9d1a47ee 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -624,7 +624,6 @@ int dns_transaction_go(DnsTransaction *t) { if (r < 0) return r; if (r > 0) { - log_debug("Cache hit!"); if (t->cached_rcode == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); else @@ -661,8 +660,6 @@ int dns_transaction_go(DnsTransaction *t) { return 0; } - log_debug("Cache miss!"); - /* Otherwise, we need to ask the network */ r = dns_transaction_make_packet(t); if (r == -EDOM) { diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 5be01d3cb8..645f2a824c 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -176,8 +176,7 @@ static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, voi break; case RTM_DELADDR: - if (a) - link_address_free(a); + link_address_free(a); break; } |