summaryrefslogtreecommitdiff
path: root/src/resolve
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve')
-rw-r--r--src/resolve/resolved-bus.c63
-rw-r--r--src/resolve/resolved-dns-answer.c102
-rw-r--r--src/resolve/resolved-dns-answer.h17
-rw-r--r--src/resolve/resolved-dns-cache.c8
-rw-r--r--src/resolve/resolved-dns-packet.c2
-rw-r--r--src/resolve/resolved-dns-query.c374
-rw-r--r--src/resolve/resolved-dns-query.h1
-rw-r--r--src/resolve/resolved-dns-question.c53
-rw-r--r--src/resolve/resolved-dns-rr.c30
-rw-r--r--src/resolve/resolved-dns-rr.h1
-rw-r--r--src/resolve/resolved-dns-scope.c39
-rw-r--r--src/resolve/resolved-dns-transaction.c3
-rw-r--r--src/resolve/resolved-dns-zone.c6
-rw-r--r--src/resolve/resolved-link.c20
-rw-r--r--src/resolve/resolved-manager.c131
-rw-r--r--src/resolve/resolved-manager.h9
16 files changed, 650 insertions, 209 deletions
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-question.c b/src/resolve/resolved-dns-question.c
index 0efe740d1a..7590bc5418 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -68,9 +68,11 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
unsigned i;
int r;
- assert(q);
assert(key);
+ if (!q)
+ return -ENOSPC;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_equal(q->keys[i], key);
if (r < 0)
@@ -90,9 +92,11 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
unsigned i;
int r;
- assert(q);
assert(rr);
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_match_rr(q->keys[i], rr);
if (r != 0)
@@ -106,9 +110,11 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
unsigned i;
int r;
- assert(q);
assert(rr);
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
r = dns_resource_key_match_cname(q->keys[i], rr);
if (r != 0)
@@ -123,7 +129,8 @@ int dns_question_is_valid(DnsQuestion *q) {
unsigned i;
int r;
- assert(q);
+ if (!q)
+ return 0;
if (q->n_keys <= 0)
return 0;
@@ -151,16 +158,19 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
unsigned j;
int r;
- assert(q);
- assert(other);
-
/* Checks if all keys in "other" are also contained in "q" */
+ if (!other)
+ return 1;
+
for (j = 0; j < other->n_keys; j++) {
DnsResourceKey *b = other->keys[j];
bool found = false;
unsigned i;
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
DnsResourceKey *a = q->keys[i];
@@ -192,9 +202,11 @@ int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
unsigned j;
int r;
- assert(a);
assert(k);
+ if (!a)
+ return 0;
+
for (j = 0; j < a->n_keys; j++) {
r = dns_resource_key_equal(a->keys[j], k);
if (r != 0)
@@ -208,8 +220,10 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
unsigned j;
int r;
- assert(a);
- assert(b);
+ if (!a)
+ return !b || b->n_keys == 0;
+ if (!b)
+ return a->n_keys == 0;
/* Checks if all keys in a are also contained b, and vice versa */
@@ -234,10 +248,19 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
unsigned i;
int r;
- assert(q);
assert(name);
assert(ret);
+ if (!q) {
+ n = dns_question_new(0);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ n = 0;
+ return 0;
+ }
+
for (i = 0; i < q->n_keys; i++) {
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
if (r < 0)
@@ -281,9 +304,11 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **
int dns_question_endswith(DnsQuestion *q, const char *suffix) {
unsigned i;
- assert(q);
assert(suffix);
+ if (!q)
+ return 1;
+
for (i = 0; i < q->n_keys; i++) {
int k;
@@ -298,10 +323,12 @@ int dns_question_endswith(DnsQuestion *q, const char *suffix) {
int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address) {
unsigned i;
- assert(q);
assert(family);
assert(address);
+ if (!q)
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
int k;
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-transaction.c b/src/resolve/resolved-dns-transaction.c
index 2d9d1a47ee..73bfbb7ed4 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -256,7 +256,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
- /* When we already received a query to this (but it was truncated), send to its sender address */
+ /* When we already received a reply to this (but it was truncated), send to its sender address */
if (t->received)
fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
else {
@@ -292,7 +292,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
return r;
}
-
dns_server_unref(t->server);
t->server = dns_server_ref(server);
t->received = dns_packet_unref(t->received);
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_;