diff options
-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_; |