Systemd/src/resolve/resolved-dns-synthesize.c
Lennart Poettering 0c69794138 tree-wide: remove Lennart's copyright lines
These lines are generally out-of-date, incomplete and unnecessary. With
SPDX and git repository much more accurate and fine grained information
about licensing and authorship is available, hence let's drop the
per-file copyright notice. Of course, removing copyright lines of others
is problematic, hence this commit only removes my own lines and leaves
all others untouched. It might be nicer if sooner or later those could
go away too, making git the only and accurate source of authorship
information.
2018-06-14 10:20:20 +02:00

434 lines
14 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-synthesize.h"
int dns_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 and we
* only return localhost RRs, we synthesize the answer for
* localhost. */
if (ifindex > 0)
return ifindex;
return LOOPBACK_IFINDEX;
}
int dns_synthesize_family(uint64_t flags) {
/* Picks an address family depending on set flags. This is
* purely for synthesized answers, where the family we return
* for the reply should match what was requested in the
* question, even though we are synthesizing the answer
* here. */
if (!(flags & SD_RESOLVED_DNS)) {
if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
return AF_INET;
if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
return AF_INET6;
}
return AF_UNSPEC;
}
DnsProtocol dns_synthesize_protocol(uint64_t flags) {
/* Similar as dns_synthesize_family() but does this for the
* protocol. If resolving via DNS was requested, we claim it
* was DNS. Similar, if nothing specific was
* requested. However, if only resolving via LLMNR was
* requested we return that. */
if (flags & SD_RESOLVED_DNS)
return DNS_PROTOCOL_DNS;
if (flags & SD_RESOLVED_LLMNR)
return DNS_PROTOCOL_LLMNR;
if (flags & SD_RESOLVED_MDNS)
return DNS_PROTOCOL_MDNS;
return DNS_PROTOCOL_DNS;
}
static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
int r;
assert(m);
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, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
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, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
}
return 0;
}
static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
_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, flags);
}
static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
int r;
assert(m);
assert(key);
assert(answer);
if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
r = dns_answer_reserve(answer, 1);
if (r < 0)
return r;
r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
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, DNS_ANSWER_AUTHENTICATED);
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) {
bool added = false;
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, DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
added = true;
}
return added;
}
static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n = 0, af;
assert(m);
assert(key);
assert(answer);
af = dns_type_to_af(key->type);
if (af >= 0) {
n = local_addresses(m->rtnl, 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 (IN_SET(af, AF_INET, AF_UNSPEC))
buffer[n++] = (struct local_address) {
.family = AF_INET,
.ifindex = dns_synthesize_ifindex(ifindex),
.address.in.s_addr = htobe32(0x7F000002),
};
if (IN_SET(af, AF_INET6, AF_UNSPEC))
buffer[n++] = (struct local_address) {
.family = AF_INET6,
.ifindex = dns_synthesize_ifindex(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(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
bool added = false;
int n, r;
assert(m);
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, 4);
if (r < 0)
return r;
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
if (r < 0)
return r;
return 1;
}
n = local_addresses(m->rtnl, ifindex, af, &addresses);
if (n <= 0)
return n;
r = answer_add_addresses_ptr(answer, m->full_hostname, addresses, n, af, address);
if (r < 0)
return r;
if (r > 0)
added = true;
r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
if (r < 0)
return r;
if (r > 0)
added = true;
r = answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
if (r < 0)
return r;
if (r > 0)
added = true;
return added;
}
static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n = 0, af, r;
assert(m);
assert(key);
assert(answer);
af = dns_type_to_af(key->type);
if (af >= 0) {
n = local_gateways(m->rtnl, ifindex, af, &addresses);
if (n <= 0)
return n; /* < 0 means: error; == 0 means we have no gateway */
}
r = answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
if (r < 0)
return r;
return 1; /* > 0 means: we have some gateway */
}
static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n;
assert(m);
assert(address);
assert(answer);
n = local_gateways(m->rtnl, ifindex, af, &addresses);
if (n <= 0)
return n;
return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address);
}
int dns_synthesize_answer(
Manager *m,
DnsQuestion *q,
int ifindex,
DnsAnswer **ret) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
DnsResourceKey *key;
bool found = false, nxdomain = false;
int r;
assert(m);
assert(q);
DNS_QUESTION_FOREACH(key, q) {
union in_addr_union address;
const char *name;
int af;
if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY))
continue;
name = dns_resource_key_name(key);
if (is_localhost(name)) {
r = synthesize_localhost_rr(m, key, ifindex, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
} else if (manager_is_own_hostname(m, name)) {
r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
} else if (is_gateway_hostname(name)) {
r = synthesize_gateway_rr(m, key, ifindex, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
if (r == 0) { /* if we have no gateway return NXDOMAIN */
nxdomain = true;
continue;
}
} 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) {
r = synthesize_localhost_ptr(m, key, ifindex, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
} else if (dns_name_address(name, &af, &address) > 0) {
int v, w;
v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
if (v < 0)
return log_error_errno(v, "Failed to synthesize system hostname PTR RR: %m");
w = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
if (w < 0)
return log_error_errno(w, "Failed to synthesize gateway hostname PTR RR: %m");
if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */
continue;
} else
continue;
found = true;
}
if (found) {
if (ret)
*ret = TAKE_PTR(answer);
return 1;
} else if (nxdomain)
return -ENXIO;
return 0;
}