diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-08-17 23:54:08 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-08-21 12:41:08 +0200 |
commit | 78c6a153c47f8d597c827bdcaf8c4e42ac87f738 (patch) | |
tree | c0b378f70987c6de3cbbcc02fd6cc2c6549d2a3a | |
parent | 8013e860b6344cb109e68208a3a91b0fc3cb9ed1 (diff) |
resolved: rework synthesizing logic
With this change we'll now also generate synthesized RRs for the local
LLMNR hostname (first label of system hostname), the local mDNS hostname
(first label of system hostname suffixed with .local), the "gateway"
hostname and all the reverse PTRs. This hence takes over part of what
nss-myhostname already implemented.
Local hostnames resolve to the set of local IP addresses. Since the
addresses are possibly on different interfaces it is necessary to change
the internal DnsAnswer object to track per-RR interface indexes, and to
change the bus API to always return the interface per-address rather than
per-reply. This change also patches the existing clients for resolved
accordingly (nss-resolve + systemd-resolve-host).
This also changes the routing logic for queries slightly: we now ensure
that the local hostname is never resolved via LLMNR, thus making it
trustable on the local system.
-rw-r--r-- | src/nss-resolve/nss-resolve.c | 81 | ||||
-rw-r--r-- | src/resolve-host/resolve-host.c | 133 | ||||
-rw-r--r-- | src/resolve/resolved-bus.c | 63 | ||||
-rw-r--r-- | src/resolve/resolved-dns-answer.c | 102 | ||||
-rw-r--r-- | src/resolve/resolved-dns-answer.h | 17 | ||||
-rw-r--r-- | src/resolve/resolved-dns-cache.c | 8 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-query.c | 374 | ||||
-rw-r--r-- | src/resolve/resolved-dns-query.h | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.c | 30 | ||||
-rw-r--r-- | src/resolve/resolved-dns-rr.h | 1 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 39 | ||||
-rw-r--r-- | src/resolve/resolved-dns-zone.c | 6 | ||||
-rw-r--r-- | src/resolve/resolved-link.c | 20 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 131 | ||||
-rw-r--r-- | src/resolve/resolved-manager.h | 9 |
16 files changed, 709 insertions, 308 deletions
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index da22f98eba..ef5eb7b4cf 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -61,23 +61,21 @@ static bool bus_error_shall_fallback(sd_bus_error *e) { } static int count_addresses(sd_bus_message *m, int af, const char **canonical) { - int c = 0, r, ifindex; + int c = 0, r; assert(m); assert(canonical); - r = sd_bus_message_read(m, "i", &ifindex); + r = sd_bus_message_enter_container(m, 'a', "(iiay)"); if (r < 0) return r; - r = sd_bus_message_enter_container(m, 'a', "(iay)"); - if (r < 0) - return r; + while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { + int family, ifindex; - while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) { - int family; + assert_cc(sizeof(int32_t) == sizeof(int)); - r = sd_bus_message_read(m, "i", &family); + r = sd_bus_message_read(m, "ii", &ifindex, &family); if (r < 0) return r; @@ -126,7 +124,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( const char *canonical = NULL; size_t l, ms, idx; char *r_name; - int c, r, i = 0, ifindex; + int c, r, i = 0; assert(name); assert(pat); @@ -218,28 +216,26 @@ enum nss_status _nss_resolve_gethostbyname4_r( /* Second, append addresses */ r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) goto fail; - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int family, ifindex; const void *a; size_t sz; - r = sd_bus_message_read(reply, "i", &family); + assert_cc(sizeof(int32_t) == sizeof(int)); + + r = sd_bus_message_read(reply, "ii", &ifindex, &family); if (r < 0) goto fail; + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } + r = sd_bus_message_read_array(reply, 'y', &a, &sz); if (r < 0) goto fail; @@ -308,7 +304,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; size_t l, idx, ms, alen; const char *canonical; - int c, r, i = 0, ifindex; + int c, r, i = 0; assert(name); assert(result); @@ -420,28 +416,24 @@ enum nss_status _nss_resolve_gethostbyname3_r( /* Third, append addresses */ r_addr = buffer + idx; - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) goto fail; - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int ifindex, family; const void *a; size_t sz; - r = sd_bus_message_read(reply, "i", &family); + r = sd_bus_message_read(reply, "ii", &ifindex, &family); if (r < 0) goto fail; + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } + r = sd_bus_message_read_array(reply, 'y', &a, &sz); if (r < 0) goto fail; @@ -603,20 +595,17 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_UNAVAIL; } - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(is)"); if (r < 0) goto fail; - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - r = sd_bus_message_enter_container(reply, 'a', "s"); - if (r < 0) - goto fail; + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { c++; ms += ALIGN(strlen(n) + 1); } @@ -661,7 +650,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( /* Fourth, place aliases */ i = 0; r_name = buffer + idx; - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { char *p; size_t l; diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 7525d7c32f..3f45a9f0e8 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -41,13 +41,13 @@ static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; -static void print_source(int ifindex, uint64_t flags, usec_t rtt) { +static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; if (!arg_legend) return; - if (ifindex <= 0 && flags == 0) + if (flags == 0) return; fputs("\n-- Information acquired via", stdout); @@ -58,11 +58,6 @@ static void print_source(int ifindex, uint64_t flags, usec_t rtt) { flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : ""); - if (ifindex > 0) { - char ifname[IF_NAMESIZE] = ""; - printf(" interface %s", strna(if_indextoname(ifindex, ifname))); - } - assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); printf(" in %s", rtt_str); @@ -76,14 +71,18 @@ static int resolve_host(sd_bus *bus, const char *name) { _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *canonical = NULL; + char ifname[IF_NAMESIZE] = ""; unsigned c = 0; - int r, ifindex; + int r; uint64_t flags; usec_t ts; assert(name); - log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex); + if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); + + log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname); r = sd_bus_message_new_method_call( bus, @@ -109,22 +108,19 @@ static int resolve_host(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { const void *a; - int family; size_t sz; _cleanup_free_ char *pretty = NULL; - char ifname[IF_NAMESIZE] = ""; + int ifindex, family; + + assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(reply, "i", &family); + r = sd_bus_message_read(reply, "ii", &ifindex, &family); if (r < 0) return bus_log_parse_error(r); @@ -142,26 +138,17 @@ static int resolve_host(sd_bus *bus, const char *name) { } if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", - name, sz, af_to_name(family) ?: "unknown"); + log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); continue; } - if (ifindex > 0) { - char *t; - - t = if_indextoname(ifindex, ifname); - if (!t) { - log_error("Failed to resolve interface name for index %i", ifindex); - continue; - } - } + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); r = in_addr_to_string(family, a, &pretty); - if (r < 0) { - log_error_errno(r, "%s: failed to print address: %m", name); - continue; - } + if (r < 0) + return log_error_errno(r, "Failed to print address for %s: %m", name); printf("%*s%s %s%s%s\n", (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", @@ -192,7 +179,7 @@ static int resolve_host(sd_bus *bus, const char *name) { return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } @@ -204,7 +191,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a char ifname[IF_NAMESIZE] = ""; uint64_t flags; unsigned c = 0; - const char *n; usec_t ts; int r; @@ -212,19 +198,15 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); + if (ifindex <= 0) + ifindex = arg_ifindex; + r = in_addr_to_string(family, address, &pretty); if (r < 0) return log_oom(); - if (ifindex > 0) { - char *t; - - t = if_indextoname(ifindex, ifname); - if (!t) { - log_error("Failed to resolve interface name for index %i", ifindex); - return -errno; - } - } + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname); @@ -238,10 +220,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_append(req, "ii", ifindex, family); if (r < 0) return bus_log_create_error(r); @@ -264,19 +242,31 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'a', "s"); + r = sd_bus_message_enter_container(reply, 'a', "(is)"); if (r < 0) return bus_log_create_error(r); - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) { + const char *n; + + assert_cc(sizeof(int) == sizeof(int32_t)); - printf("%*s%s%s%s %s\n", + r = sd_bus_message_read(reply, "is", &ifindex, &n); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%*s%*s%*s%s %s\n", (int) strlen(pretty), c == 0 ? pretty : "", - isempty(ifname) ? "" : "%", ifname, + isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%", + (int) strlen(ifname), c == 0 ? ifname : "", c == 0 ? ":" : " ", n); @@ -298,7 +288,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } @@ -333,14 +323,18 @@ static int resolve_record(sd_bus *bus, const char *name) { _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + char ifname[IF_NAMESIZE] = ""; unsigned n = 0; uint64_t flags; - int r, ifindex; + int r; usec_t ts; assert(name); - log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type)); + if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); + + log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname); r = sd_bus_message_new_method_call( bus, @@ -367,23 +361,22 @@ static int resolve_record(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_enter_container(reply, 'a', "(qqay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; _cleanup_free_ char *s = NULL; uint16_t c, t; + int ifindex; const void *d; size_t l; - r = sd_bus_message_read(reply, "qq", &c, &t); + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); if (r < 0) return bus_log_parse_error(r); @@ -415,7 +408,11 @@ static int resolve_record(sd_bus *bus, const char *name) { return r; } - printf("%s\n", s); + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); n++; } if (r < 0) @@ -434,7 +431,7 @@ static int resolve_record(sd_bus *bus, const char *name) { return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 6db12511f9..de5e8e9c29 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -91,13 +91,17 @@ static int reply_query_state(DnsQuery *q) { } } -static int append_address(sd_bus_message *reply, DnsResourceRecord *rr) { +static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) { int r; assert(reply); assert(rr); - r = sd_bus_message_open_container(reply, 'r', "iay"); + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", ifindex); if (r < 0) return r; @@ -145,11 +149,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(iay)"); + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); if (r < 0) goto finish; @@ -157,27 +157,27 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) { /* Hmm, if this is not an address record, maybe it's a cname? If so, remember this */ - r = dns_question_matches_cname(q->question, answer->rrs[i]); + r = dns_question_matches_cname(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r > 0) - cname = dns_resource_record_ref(answer->rrs[i]); + cname = dns_resource_record_ref(answer->items[i].rr); continue; } - r = append_address(reply, answer->rrs[i]); + r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); if (r < 0) goto finish; if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); + canonical = dns_resource_record_ref(answer->items[i].rr); added ++; } @@ -204,18 +204,18 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { /* Before we restart the query, let's see if any of * the RRs we already got already answers our query */ for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) continue; - r = append_address(reply, answer->rrs[i]); + r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); if (r < 0) goto finish; if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); + canonical = dns_resource_record_ref(answer->items[i].rr); added++; } @@ -373,11 +373,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "s"); + r = sd_bus_message_open_container(reply, 'a', "(is)"); if (r < 0) goto finish; @@ -385,13 +381,13 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) continue; - r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name); + r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name); if (r < 0) goto finish; @@ -525,11 +521,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(qqay)"); + r = sd_bus_message_open_container(reply, 'a', "(iqqay)"); if (r < 0) goto finish; @@ -540,7 +532,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; size_t start; - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) @@ -550,15 +542,18 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = dns_packet_append_rr(p, answer->rrs[i], &start); + r = dns_packet_append_rr(p, answer->items[i].rr, &start); if (r < 0) goto finish; - r = sd_bus_message_open_container(reply, 'r', "qqay"); + r = sd_bus_message_open_container(reply, 'r', "iqqay"); if (r < 0) goto finish; - r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type); + r = sd_bus_message_append(reply, "iqq", + answer->items[i].ifindex, + answer->items[i].rr->key->class, + answer->items[i].rr->key->type); if (r < 0) goto finish; @@ -662,9 +657,9 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ResolveHostname", "isit", "ia(iay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveAddress", "iiayt", "iast", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveRecord", "isqqt", "ia(qqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index f77b98e505..13ad4ca6bd 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -25,7 +25,7 @@ DnsAnswer *dns_answer_new(unsigned n) { DnsAnswer *a; - a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n); + a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n); if (!a) return NULL; @@ -54,7 +54,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) { unsigned i; for (i = 0; i < a->n_rrs; i++) - dns_resource_record_unref(a->rrs[i]); + dns_resource_record_unref(a->items[i].rr); free(a); } else @@ -63,25 +63,30 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) { return NULL; } -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { +int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) { unsigned i; int r; - assert(a); assert(rr); + if (!a) + return -ENOSPC; + for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_record_equal(a->rrs[i], rr); + if (a->items[i].ifindex != ifindex) + continue; + + r = dns_resource_record_equal(a->items[i].rr, rr); if (r < 0) return r; if (r > 0) { /* Entry already exists, keep the entry with * the higher RR, or the one with TTL 0 */ - if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) { + if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) { dns_resource_record_ref(rr); - dns_resource_record_unref(a->rrs[i]); - a->rrs[i] = rr; + dns_resource_record_unref(a->items[i].rr); + a->items[i].rr = rr; } return 0; @@ -91,7 +96,10 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { if (a->n_rrs >= a->n_allocated) return -ENOSPC; - a->rrs[a->n_rrs++] = dns_resource_record_ref(rr); + a->items[a->n_rrs].rr = dns_resource_record_ref(rr); + a->items[a->n_rrs].ifindex = ifindex; + a->n_rrs++; + return 1; } @@ -118,18 +126,20 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) { soa->soa.expire = 1; soa->soa.minimum = ttl; - return dns_answer_add(a, soa); + return dns_answer_add(a, soa, 0); } int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { unsigned i; int r; - assert(a); assert(key); + if (!a) + return 0; + for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_key_match_rr(key, a->rrs[i]); + r = dns_resource_key_match_rr(key, a->items[i].rr); if (r < 0) return r; if (r > 0) @@ -142,24 +152,26 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) { unsigned i; - assert(a); assert(key); assert(ret); + if (!a) + return 0; + /* For a SOA record we can never find a matching SOA record */ if (key->type == DNS_TYPE_SOA) return 0; for (i = 0; i < a->n_rrs; i++) { - if (a->rrs[i]->key->class != DNS_CLASS_IN) + if (a->items[i].rr->key->class != DNS_CLASS_IN) continue; - if (a->rrs[i]->key->type != DNS_TYPE_SOA) + if (a->items[i].rr->key->type != DNS_TYPE_SOA) continue; - if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) { - *ret = a->rrs[i]; + if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) { + *ret = a->items[i].rr; return 1; } } @@ -184,7 +196,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { if (a) { for (i = 0; i < a->n_rrs; i++) { - r = dns_answer_add(ret, a->rrs[i]); + r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex); if (r < 0) return NULL; } @@ -192,7 +204,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { if (b) { for (i = 0; i < b->n_rrs; i++) { - r = dns_answer_add(ret, b->rrs[i]); + r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex); if (r < 0) return NULL; } @@ -205,9 +217,11 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { } void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) { - DnsResourceRecord **rrs; + DnsAnswerItem *items; unsigned i, start, end; - assert(a); + + if (!a) + return; if (a->n_rrs <= 1) return; @@ -218,19 +232,51 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) { /* RFC 4795, Section 2.6 suggests we should order entries * depending on whether the sender is a link-local address. */ - rrs = newa(DnsResourceRecord*, a->n_rrs); + items = newa(DnsAnswerItem, a->n_rrs); for (i = 0; i < a->n_rrs; i++) { - if (a->rrs[i]->key->class == DNS_CLASS_IN && - ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) || - (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local))) + if (a->items[i].rr->key->class == DNS_CLASS_IN && + ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) || + (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local))) /* Order address records that are are not preferred to the end of the array */ - rrs[end--] = a->rrs[i]; + items[end--] = a->items[i]; else /* Order all other records to the beginning of the array */ - rrs[start++] = a->rrs[i]; + items[start++] = a->items[i]; } assert(start == end+1); - memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs); + memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs); +} + +int dns_answer_reserve(DnsAnswer **a, unsigned n_free) { + DnsAnswer *n; + + if (n_free <= 0) + return 0; + + if (*a) { + unsigned ns; + + if ((*a)->n_ref > 1) + return -EBUSY; + + ns = (*a)->n_rrs + n_free; + + if ((*a)->n_allocated >= ns) + return 0; + + n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns); + if (!n) + return -ENOMEM; + + n->n_allocated = ns; + } else { + n = dns_answer_new(n_free); + if (!n) + return -ENOMEM; + } + + *a = n; + return 0; } diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index af3e462ed5..0757dd60d0 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -22,22 +22,31 @@ ***/ typedef struct DnsAnswer DnsAnswer; +typedef struct DnsAnswerItem DnsAnswerItem; #include "resolved-dns-rr.h" -/* A simple array of resource records */ +/* A simple array of resource records. We keep track of the + * originating ifindex for each RR where that makes sense, so that we + * can qualify A and AAAA RRs referring to a local link with the + * right ifindex. */ + +struct DnsAnswerItem { + DnsResourceRecord *rr; + int ifindex; +}; struct DnsAnswer { unsigned n_ref; unsigned n_rrs, n_allocated; - DnsResourceRecord* rrs[0]; + DnsAnswerItem items[0]; }; DnsAnswer *dns_answer_new(unsigned n); DnsAnswer *dns_answer_ref(DnsAnswer *a); DnsAnswer *dns_answer_unref(DnsAnswer *a); -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr); +int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex); int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl); int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key); int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret); @@ -45,4 +54,6 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b); void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local); +int dns_answer_reserve(DnsAnswer **a, unsigned n_free); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 81ea1cafcb..eb51b4b895 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -426,7 +426,7 @@ int dns_cache_put( return 0; for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); /* We only care for positive replies and NXDOMAINs, on all * other replies we will simply flush the respective entries, @@ -443,7 +443,7 @@ int dns_cache_put( /* 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, owner_family, owner_address); + r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address); if (r < 0) goto fail; } @@ -478,7 +478,7 @@ fail: for (i = 0; i < q->n_keys; i++) dns_cache_remove(c, q->keys[i]); for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); return r; } @@ -566,7 +566,7 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { j = hashmap_get(c->by_key, q->keys[i]); LIST_FOREACH(by_key, j, j) { if (j->rr) { - r = dns_answer_add(answer, j->rr); + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 35ad899544..ad337c2714 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1795,7 +1795,7 @@ int dns_packet_extract(DnsPacket *p) { if (r < 0) goto finish; - r = dns_answer_add(answer, rr); + r = dns_answer_add(answer, rr, p->ifindex); if (r < 0) goto finish; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 3ada442006..4f3f903548 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -21,6 +21,7 @@ #include "hostname-util.h" #include "dns-domain.h" +#include "local-addresses.h" #include "resolved-dns-query.h" @@ -216,9 +217,10 @@ static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { 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 + /* When the caller asked for resolving on a specific + * interface, we synthesize the answer for that + * interface. However, if nothing specific was claimed and we + * only return localhost RRs, we synthesize the answer for * localhost. */ if (ifindex > 0) @@ -261,7 +263,289 @@ static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) { return DNS_PROTOCOL_DNS; } -static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { +static int dns_type_to_af(uint16_t t) { + switch (t) { + + case DNS_TYPE_A: + return AF_INET; + + case DNS_TYPE_AAAA: + return AF_INET6; + + case DNS_TYPE_ANY: + return AF_UNSPEC; + + default: + return -EINVAL; + } +} + +static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + int r; + + assert(q); + assert(key); + assert(answer); + + r = dns_answer_reserve(answer, 2); + if (r < 0) + return r; + + if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key)); + if (!rr) + return -ENOMEM; + + rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); + + r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key)); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = in6addr_loopback; + + r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from); + if (!rr) + return -ENOMEM; + + rr->ptr.name = strdup(to); + if (!rr->ptr.name) + return -ENOMEM; + + return dns_answer_add(*answer, rr, ifindex); +} + +static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + int r; + + assert(q); + assert(key); + assert(answer); + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) { + r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_rr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + r = dns_answer_reserve(answer, n_addresses); + if (r < 0) + return r; + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_ptr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses, + int af, const union in_addr_union *match) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + if (af != AF_UNSPEC) { + + if (addresses[j].family != af) + continue; + + if (match && !in_addr_equal(af, match, &addresses[j].address)) + continue; + } + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex); + if (r < 0) + return r; + } + + return 0; +} + +static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(q); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + if (n == 0) { + struct local_address buffer[2]; + + /* If we have no local addresses then use ::1 + * and 127.0.0.2 as local ones. */ + + if (af == AF_INET || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET, + .ifindex = SYNTHESIZE_IFINDEX(q->ifindex), + .address.in.s_addr = htobe32(0x7F000002), + }; + + if (af == AF_INET6 || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET6, + .ifindex = SYNTHESIZE_IFINDEX(q->ifindex), + .address.in6 = in6addr_loopback, + }; + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n); + } + } + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); +} + +static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n, r; + + assert(q); + assert(address); + assert(answer); + + if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { + + /* Always map the IPv4 address 127.0.0.2 to the local + * hostname, in addition to "localhost": */ + + r = dns_answer_reserve(answer, 3); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + return 0; + } + + n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address); + if (r < 0) + return r; + + return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address); +} + +static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(q); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + } + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); +} + +static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n; + + assert(q); + assert(address); + assert(answer); + + n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address); +} + +static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; unsigned i; int r; @@ -276,11 +560,12 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) DNS_TRANSACTION_NO_SERVERS, DNS_TRANSACTION_TIMEOUT, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED)) - return; + return 0; for (i = 0; i < q->question->n_keys; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + union in_addr_union address; const char *name; + int af; if (q->question->keys[i]->class != DNS_CLASS_IN && q->question->keys[i]->class != DNS_CLASS_ANY) @@ -290,78 +575,55 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) 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; - } + r = synthesize_localhost_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); - rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); - break; + } else if (manager_is_own_hostname(q->manager, name)) { - case DNS_TYPE_AAAA: - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); - if (!rr) { - log_oom(); - return; - } + r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize system hostname RRs: %m"); - rr->aaaa.in6_addr = in6addr_loopback; - break; - } + } else if (is_gateway_hostname(name)) { - } 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)) { + r = synthesize_gateway_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize gateway RRs: %m"); - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, name); - if (!rr) { - log_oom(); - return; - } + } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.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->ptr.name = strdup("localhost"); - if (!rr->ptr.name) { - log_oom(); - return; - } - } + r = synthesize_localhost_ptr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); - if (!rr) - continue; + } else if (dns_name_address(name, &af, &address) > 0) { - if (!answer) { - answer = dns_answer_new(q->question->n_keys); - if (!answer) { - log_oom(); - return; - } - } + r = synthesize_system_hostname_ptr(q, af, &address, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m"); - r = dns_answer_add(answer, rr); - if (r < 0) { - log_error_errno(r, "Failed to add synthetic RR to answer: %m"); - return; + r = synthesize_gateway_ptr(q, af, &address, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m"); } } if (!answer) - return; + return 0; 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; + + return 1; } int dns_query_go(DnsQuery *q) { @@ -437,7 +699,6 @@ int dns_query_go(DnsQuery *q) { } q->answer = dns_answer_unref(q->answer); - q->answer_ifindex = 0; q->answer_rcode = 0; q->answer_family = AF_UNSPEC; q->answer_protocol = _DNS_PROTOCOL_INVALID; @@ -584,7 +845,6 @@ void dns_query_ready(DnsQuery *q) { if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) { q->answer = dns_answer_ref(answer); q->answer_rcode = rcode; - q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0; q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID; q->answer_family = scope ? scope->family : AF_UNSPEC; } diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 5a319f0a62..93d49301fa 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -45,7 +45,6 @@ struct DnsQuery { /* Discovered data */ DnsAnswer *answer; - int answer_ifindex; int answer_family; DnsProtocol answer_protocol; int answer_rcode; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index ad7ca26cfe..f31644eebc 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -350,6 +350,36 @@ int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const u return 0; } +int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) { + DnsResourceRecord *rr; + + assert(ret); + assert(address); + assert(family); + + if (family == AF_INET) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); + if (!rr) + return -ENOMEM; + + rr->a.in_addr = address->in; + + } else if (family == AF_INET6) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = address->in6; + } else + return -EAFNOSUPPORT; + + *ret = rr; + + return 0; +} + int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { int r; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 0f40f3ceef..8986a298af 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -191,6 +191,7 @@ DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, c DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr); int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); +int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 57d9071dfc..b1e5855a6f 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -328,11 +328,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (dns_name_root(domain) != 0) return DNS_SCOPE_NO; - if (is_localhost(domain)) - return DNS_SCOPE_NO; - - /* Never resolve any loopback IP address via DNS, LLMNR or mDNS */ - if (dns_name_endswith(domain, "127.in-addr.arpa") > 0 || + /* Never resolve any loopback hostname or IP address via DNS, + * LLMNR or mDNS. Instead, always rely on synthesized RRs for + * these. */ + if (is_localhost(domain) || + dns_name_endswith(domain, "127.in-addr.arpa") > 0 || 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; @@ -350,19 +350,22 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co } if (s->protocol == DNS_PROTOCOL_MDNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 || - (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0)) + if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || + (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ + dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */ + manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */ return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; } if (s->protocol == DNS_PROTOCOL_LLMNR) { - if (dns_name_endswith(domain, "in-addr.arpa") > 0 || - dns_name_endswith(domain, "ip6.arpa") > 0 || - (dns_name_single_label(domain) > 0 && - dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ + if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || + (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */ + !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ + manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; @@ -490,7 +493,7 @@ static int dns_scope_make_reply_packet( if (answer) { for (i = 0; i < answer->n_rrs; i++) { - r = dns_packet_append_rr(p, answer->rrs[i], NULL); + r = dns_packet_append_rr(p, answer->items[i].rr, NULL); if (r < 0) return r; } @@ -500,7 +503,7 @@ static int dns_scope_make_reply_packet( if (soa) { for (i = 0; i < soa->n_rrs; i++) { - r = dns_packet_append_rr(p, soa->rrs[i], NULL); + r = dns_packet_append_rr(p, soa->items[i].rr, NULL); if (r < 0) return r; } @@ -525,7 +528,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { 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); + dns_zone_verify_conflicts(&s->zone, p->answer->items[n].rr->key); } void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { @@ -793,16 +796,16 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { /* 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]); + r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr); 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); + r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender); if (r <= 0) continue; - dns_scope_notify_conflict(scope, p->answer->rrs[i]); + dns_scope_notify_conflict(scope, p->answer->items[i].rr); } } diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 32d771a954..99d96c3f40 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -422,7 +422,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe if (k < 0) return k; if (k > 0) { - r = dns_answer_add(answer, j->rr); + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; @@ -448,7 +448,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe if (j->state != DNS_ZONE_ITEM_PROBING) tentative = false; - r = dns_answer_add(answer, j->rr); + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; } @@ -505,7 +505,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) { i->state = DNS_ZONE_ITEM_WITHDRAWN; /* Maybe change the hostname */ - if (dns_name_equal(i->scope->manager->hostname, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) + if (manager_is_own_hostname(i->scope->manager, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) manager_next_hostname(i->scope->manager); } diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index d66b3a88fc..47f461a37d 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -419,16 +419,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->link->llmnr_support == SUPPORT_YES && a->link->manager->llmnr_support == SUPPORT_YES) { - if (!a->link->manager->host_ipv4_key) { - a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname); - if (!a->link->manager->host_ipv4_key) { + if (!a->link->manager->llmnr_host_ipv4_key) { + a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname); + if (!a->link->manager->llmnr_host_ipv4_key) { r = -ENOMEM; goto fail; } } if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key); + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key); if (!a->llmnr_address_rr) { r = -ENOMEM; goto fail; @@ -439,7 +439,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { } if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); if (r < 0) goto fail; @@ -476,16 +476,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->link->llmnr_support == SUPPORT_YES && a->link->manager->llmnr_support == SUPPORT_YES) { - if (!a->link->manager->host_ipv6_key) { - a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname); - if (!a->link->manager->host_ipv6_key) { + if (!a->link->manager->llmnr_host_ipv6_key) { + a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname); + if (!a->link->manager->llmnr_host_ipv6_key) { r = -ENOMEM; goto fail; } } if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key); + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key); if (!a->llmnr_address_rr) { r = -ENOMEM; goto fail; @@ -496,7 +496,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { } if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); if (r < 0) goto fail; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 645f2a824c..9f451dd3e8 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -310,51 +310,84 @@ static int manager_network_monitor_listen(Manager *m) { return 0; } -static int determine_hostname(char **ret) { +static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL; - int r; + char label[DNS_LABEL_MAX]; + const char *p; + int r, k; - assert(ret); + assert(llmnr_hostname); + assert(mdns_hostname); + + /* Extract and normalize the first label of the locally + * configured hostname, and check it's not "localhost". */ h = gethostname_malloc(); if (!h) return log_oom(); - if (!utf8_is_valid(h)) { + p = h; + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return log_error_errno(r, "Failed to unescape host name: %m"); + if (r == 0) { + log_error("Couldn't find a single label in hosntame."); + return -EINVAL; + } + + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + return log_error_errno(k, "Failed to undo IDNA: %m"); + if (k > 0) + r = k; + + if (!utf8_is_valid(label)) { log_error("System hostname is not UTF-8 clean."); return -EINVAL; } - r = dns_name_normalize(h, &n); - if (r < 0) { - log_error("System hostname '%s' cannot be normalized.", h); - return r; + r = dns_label_escape(label, r, &n); + if (r < 0) + return log_error_errno(r, "Failed to escape host name: %m"); + + if (is_localhost(n)) { + log_debug("System hostname is 'localhost', ignoring."); + return -EINVAL; } - *ret = n; + r = dns_name_concat(n, "local", mdns_hostname); + if (r < 0) + return log_error_errno(r, "Failed to determine mDNS hostname: %m"); + + *llmnr_hostname = n; n = NULL; return 0; } static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *h = NULL; + _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; Manager *m = userdata; int r; assert(m); - r = determine_hostname(&h); + r = determine_hostname(&llmnr_hostname, &mdns_hostname); if (r < 0) return 0; /* ignore invalid hostnames */ - if (streq(h, m->hostname)) + if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) return 0; - log_info("System hostname changed to '%s'.", h); - free(m->hostname); - m->hostname = h; - h = NULL; + log_info("System hostname changed to '%s'.", llmnr_hostname); + + free(m->llmnr_hostname); + free(m->mdns_hostname); + + m->llmnr_hostname = llmnr_hostname; + m->mdns_hostname = mdns_hostname; + + llmnr_hostname = mdns_hostname = NULL; manager_refresh_rrs(m); @@ -381,14 +414,18 @@ static int manager_watch_hostname(Manager *m) { return log_error_errno(r, "Failed to add hostname event source: %m"); } - r = determine_hostname(&m->hostname); + r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname); if (r < 0) { log_info("Defaulting to hostname 'linux'."); - m->hostname = strdup("linux"); - if (!m->hostname) + m->llmnr_hostname = strdup("linux"); + if (!m->llmnr_hostname) + return log_oom(); + + m->mdns_hostname = strdup("linux.local"); + if (!m->mdns_hostname) return log_oom(); } else - log_info("Using system hostname '%s'.", m->hostname); + log_info("Using system hostname '%s'.", m->llmnr_hostname); return 0; } @@ -492,12 +529,13 @@ Manager *manager_free(Manager *m) { sd_event_unref(m->event); - dns_resource_key_unref(m->host_ipv4_key); - dns_resource_key_unref(m->host_ipv6_key); + dns_resource_key_unref(m->llmnr_host_ipv4_key); + dns_resource_key_unref(m->llmnr_host_ipv6_key); safe_close(m->hostname_fd); sd_event_source_unref(m->hostname_event_source); - free(m->hostname); + free(m->llmnr_hostname); + free(m->mdns_hostname); free(m); @@ -1229,8 +1267,8 @@ void manager_refresh_rrs(Manager *m) { assert(m); - m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key); - m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key); + m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); + m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); HASHMAP_FOREACH(l, m->links, i) { link_add_rrs(l, true); @@ -1241,14 +1279,15 @@ void manager_refresh_rrs(Manager *m) { int manager_next_hostname(Manager *m) { const char *p; uint64_t u, a; - char *h; + char *h, *k; + int r; assert(m); - p = strchr(m->hostname, 0); + p = strchr(m->llmnr_hostname, 0); assert(p); - while (p > m->hostname) { + while (p > m->llmnr_hostname) { if (!strchr("0123456789", p[-1])) break; @@ -1268,13 +1307,22 @@ int manager_next_hostname(Manager *m) { random_bytes(&a, sizeof(a)); u += 1 + a % 10; - if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0) + if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0) return -ENOMEM; - log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h); + r = dns_name_concat(h, "local", &k); + if (r < 0) { + free(h); + return r; + } + + log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h); + + free(m->llmnr_hostname); + m->llmnr_hostname = h; - free(m->hostname); - m->hostname = h; + free(m->mdns_hostname); + m->mdns_hostname = k; manager_refresh_rrs(m); @@ -1356,6 +1404,25 @@ void manager_flush_dns_servers(Manager *m, DnsServerType t) { } } +int manager_is_own_hostname(Manager *m, const char *name) { + _cleanup_free_ char *l = NULL; + int r; + + assert(m); + assert(name); + + if (m->llmnr_hostname) { + r = dns_name_equal(name, m->llmnr_hostname); + if (r != 0) + return r; + } + + if (m->mdns_hostname) + return dns_name_equal(name, m->mdns_hostname); + + return 0; +} + 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 53b5acb33c..6f7972bbf3 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -91,9 +91,10 @@ struct Manager { sd_event_source *bus_retry_event_source; /* The hostname we publish on LLMNR and mDNS */ - char *hostname; - DnsResourceKey *host_ipv4_key; - DnsResourceKey *host_ipv6_key; + char *llmnr_hostname; + char *mdns_hostname; + DnsResourceKey *llmnr_host_ipv4_key; + DnsResourceKey *llmnr_host_ipv6_key; /* Watch the system hostname */ int hostname_fd; @@ -140,5 +141,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define EXTRA_CMSG_SPACE 1024 +int manager_is_own_hostname(Manager *m, const char *name); + const char* support_to_string(Support p) _const_; int support_from_string(const char *s) _pure_; |