summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-query.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve/resolved-dns-query.c')
-rw-r--r--src/resolve/resolved-dns-query.c374
1 files changed, 317 insertions, 57 deletions
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;
}