2014-07-16 00:26:02 +02:00
|
|
|
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
|
|
|
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2014 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-08-14 13:17:05 +02:00
|
|
|
#include "dns-domain.h"
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "hostname-util.h"
|
2015-08-17 23:54:08 +02:00
|
|
|
#include "local-addresses.h"
|
2014-07-16 00:26:02 +02:00
|
|
|
#include "resolved-dns-query.h"
|
|
|
|
|
2014-07-30 01:47:48 +02:00
|
|
|
/* How long to wait for the query in total */
|
2014-07-16 00:26:02 +02:00
|
|
|
#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
|
2014-07-30 01:47:48 +02:00
|
|
|
|
2014-07-16 22:09:00 +02:00
|
|
|
#define CNAME_MAX 8
|
2014-07-17 01:58:14 +02:00
|
|
|
#define QUERIES_MAX 2048
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
#define AUXILIARY_QUERIES_MAX 64
|
2014-07-16 22:09:00 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
static void dns_query_stop(DnsQuery *q) {
|
|
|
|
DnsTransaction *t;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(q);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
while ((t = set_steal_first(q->transactions))) {
|
|
|
|
set_remove(t->queries, q);
|
|
|
|
dns_transaction_gc(t);
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DnsQuery *dns_query_free(DnsQuery *q) {
|
|
|
|
if (!q)
|
|
|
|
return NULL;
|
|
|
|
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
while (q->auxiliary_queries)
|
|
|
|
dns_query_free(q->auxiliary_queries);
|
|
|
|
|
|
|
|
if (q->auxiliary_for) {
|
|
|
|
assert(q->auxiliary_for->n_auxiliary_queries > 0);
|
|
|
|
q->auxiliary_for->n_auxiliary_queries--;
|
|
|
|
LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
|
|
|
|
}
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
dns_query_stop(q);
|
|
|
|
set_free(q->transactions);
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
dns_question_unref(q->question);
|
|
|
|
dns_answer_unref(q->answer);
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
sd_bus_message_unref(q->request);
|
2014-08-06 16:32:55 +02:00
|
|
|
sd_bus_track_unref(q->bus_track);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-17 01:58:14 +02:00
|
|
|
if (q->manager) {
|
2014-07-16 00:26:02 +02:00
|
|
|
LIST_REMOVE(queries, q->manager->dns_queries, q);
|
2014-07-17 01:58:14 +02:00
|
|
|
q->manager->n_dns_queries--;
|
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
free(q);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-14 01:00:15 +02:00
|
|
|
int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
|
2014-07-16 00:26:02 +02:00
|
|
|
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
|
2014-07-22 21:48:41 +02:00
|
|
|
unsigned i;
|
|
|
|
int r;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
assert(m);
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(question);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-24 01:25:24 +01:00
|
|
|
r = dns_question_is_valid_for_query(question);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-17 01:58:14 +02:00
|
|
|
if (m->n_dns_queries >= QUERIES_MAX)
|
|
|
|
return -EBUSY;
|
|
|
|
|
2014-07-16 00:26:02 +02:00
|
|
|
q = new0(DnsQuery, 1);
|
|
|
|
if (!q)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
q->question = dns_question_ref(question);
|
2014-08-14 01:00:15 +02:00
|
|
|
q->ifindex = ifindex;
|
|
|
|
q->flags = flags;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
for (i = 0; i < question->n_keys; i++) {
|
2014-07-30 20:39:52 +02:00
|
|
|
_cleanup_free_ char *p;
|
|
|
|
|
|
|
|
r = dns_resource_key_to_string(question->keys[i], &p);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
log_debug("Looking up RR for %s", p);
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LIST_PREPEND(queries, m->dns_queries, q);
|
2014-07-17 01:58:14 +02:00
|
|
|
m->n_dns_queries++;
|
2014-07-16 00:26:02 +02:00
|
|
|
q->manager = m;
|
|
|
|
|
2014-07-16 22:09:00 +02:00
|
|
|
if (ret)
|
|
|
|
*ret = q;
|
|
|
|
q = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
|
|
|
|
assert(q);
|
|
|
|
assert(auxiliary_for);
|
|
|
|
|
|
|
|
/* Ensure that that the query is not auxiliary yet, and
|
|
|
|
* nothing else is auxiliary to it either */
|
|
|
|
assert(!q->auxiliary_for);
|
|
|
|
assert(!q->auxiliary_queries);
|
|
|
|
|
|
|
|
/* Ensure that the unit we shall be made auxiliary for isn't
|
|
|
|
* auxiliary itself */
|
|
|
|
assert(!auxiliary_for->auxiliary_for);
|
|
|
|
|
|
|
|
if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
|
|
|
|
q->auxiliary_for = auxiliary_for;
|
|
|
|
|
|
|
|
auxiliary_for->n_auxiliary_queries++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
|
2014-07-16 22:09:00 +02:00
|
|
|
assert(q);
|
2014-07-31 17:46:40 +02:00
|
|
|
assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
|
|
|
|
assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
|
2014-07-16 22:09:00 +02:00
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
/* Note that this call might invalidate the query. Callers
|
|
|
|
* should hence not attempt to access the query or transaction
|
|
|
|
* after calling this function. */
|
2014-07-16 22:09:00 +02:00
|
|
|
|
|
|
|
q->state = state;
|
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
dns_query_stop(q);
|
|
|
|
if (q->complete)
|
|
|
|
q->complete(q);
|
2014-07-16 22:09:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
|
|
|
DnsQuery *q = userdata;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
assert(q);
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
dns_query_complete(q, DNS_TRANSACTION_TIMEOUT);
|
2014-07-16 22:09:00 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
|
2014-07-31 17:46:40 +02:00
|
|
|
DnsTransaction *t;
|
2014-07-22 21:48:41 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
2014-07-31 17:46:40 +02:00
|
|
|
assert(s);
|
2015-08-21 22:59:38 +02:00
|
|
|
assert(key);
|
2014-07-22 21:48:41 +02:00
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = set_ensure_allocated(&q->transactions, NULL);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-08-21 22:55:01 +02:00
|
|
|
t = dns_scope_find_transaction(s, key, true);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (!t) {
|
2015-08-21 22:55:01 +02:00
|
|
|
r = dns_transaction_new(&t, s, key);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = set_ensure_allocated(&t->queries, NULL);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
2014-07-31 17:46:40 +02:00
|
|
|
goto gc;
|
2014-07-22 21:48:41 +02:00
|
|
|
|
|
|
|
r = set_put(t->queries, q);
|
|
|
|
if (r < 0)
|
2014-07-31 17:46:40 +02:00
|
|
|
goto gc;
|
2014-07-22 21:48:41 +02:00
|
|
|
|
|
|
|
r = set_put(q->transactions, t);
|
|
|
|
if (r < 0) {
|
|
|
|
set_remove(t->queries, q);
|
2014-07-31 17:46:40 +02:00
|
|
|
goto gc;
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
gc:
|
|
|
|
dns_transaction_gc(t);
|
2014-07-22 21:48:41 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
|
2015-08-21 22:59:38 +02:00
|
|
|
unsigned i;
|
2014-07-23 01:59:36 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
assert(s);
|
|
|
|
|
2015-08-21 22:59:38 +02:00
|
|
|
/* Create one transaction per question key */
|
|
|
|
|
|
|
|
for (i = 0; i < q->question->n_keys; i++) {
|
|
|
|
r = dns_query_add_transaction(q, s, q->question->keys[i]);
|
2014-07-23 01:59:36 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-14 13:17:05 +02:00
|
|
|
static int SYNTHESIZE_IFINDEX(int ifindex) {
|
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
/* 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
|
2015-08-14 13:17:05 +02:00
|
|
|
* localhost. */
|
|
|
|
|
|
|
|
if (ifindex > 0)
|
|
|
|
return ifindex;
|
|
|
|
|
|
|
|
return LOOPBACK_IFINDEX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int 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)
|
|
|
|
return AF_INET;
|
|
|
|
if (flags & SD_RESOLVED_LLMNR_IPV6)
|
|
|
|
return AF_INET6;
|
|
|
|
}
|
|
|
|
|
|
|
|
return AF_UNSPEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
|
|
|
|
|
|
|
|
/* Similar as 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;
|
|
|
|
|
|
|
|
return DNS_PROTOCOL_DNS;
|
|
|
|
}
|
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
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) {
|
2015-08-14 13:17:05 +02:00
|
|
|
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
|
|
|
unsigned i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
assert(state);
|
|
|
|
|
|
|
|
/* Tries to synthesize localhost RR replies where appropriate */
|
|
|
|
|
|
|
|
if (!IN_SET(*state,
|
|
|
|
DNS_TRANSACTION_FAILURE,
|
|
|
|
DNS_TRANSACTION_NO_SERVERS,
|
|
|
|
DNS_TRANSACTION_TIMEOUT,
|
|
|
|
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
|
2015-08-17 23:54:08 +02:00
|
|
|
return 0;
|
2015-08-14 13:17:05 +02:00
|
|
|
|
|
|
|
for (i = 0; i < q->question->n_keys; i++) {
|
2015-08-17 23:54:08 +02:00
|
|
|
union in_addr_union address;
|
2015-08-14 13:17:05 +02:00
|
|
|
const char *name;
|
2015-08-17 23:54:08 +02:00
|
|
|
int af;
|
2015-08-14 13:17:05 +02:00
|
|
|
|
|
|
|
if (q->question->keys[i]->class != DNS_CLASS_IN &&
|
|
|
|
q->question->keys[i]->class != DNS_CLASS_ANY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
|
|
|
|
|
|
|
|
if (is_localhost(name)) {
|
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
} else if (manager_is_own_hostname(q->manager, name)) {
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
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");
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
} else if (is_gateway_hostname(name)) {
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
r = synthesize_gateway_rr(q, q->question->keys[i], &answer);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
} 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) {
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
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");
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
} else if (dns_name_address(name, &af, &address) > 0) {
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
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");
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
r = synthesize_gateway_ptr(q, af, &address, &answer);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
|
2015-08-14 13:17:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!answer)
|
2015-08-17 23:54:08 +02:00
|
|
|
return 0;
|
2015-08-14 13:17:05 +02:00
|
|
|
|
|
|
|
dns_answer_unref(q->answer);
|
|
|
|
q->answer = answer;
|
|
|
|
answer = NULL;
|
|
|
|
|
|
|
|
q->answer_family = SYNTHESIZE_FAMILY(q->flags);
|
|
|
|
q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
|
|
|
|
q->answer_rcode = DNS_RCODE_SUCCESS;
|
|
|
|
|
|
|
|
*state = DNS_TRANSACTION_SUCCESS;
|
2015-08-17 23:54:08 +02:00
|
|
|
|
|
|
|
return 1;
|
2015-08-14 13:17:05 +02:00
|
|
|
}
|
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
int dns_query_go(DnsQuery *q) {
|
2014-07-16 22:09:00 +02:00
|
|
|
DnsScopeMatch found = DNS_SCOPE_NO;
|
|
|
|
DnsScope *s, *first = NULL;
|
2014-07-31 17:46:40 +02:00
|
|
|
DnsTransaction *t;
|
2014-07-22 21:48:41 +02:00
|
|
|
const char *name;
|
|
|
|
Iterator i;
|
2014-07-16 22:09:00 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
if (q->state != DNS_TRANSACTION_NULL)
|
2014-07-16 22:09:00 +02:00
|
|
|
return 0;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(q->question);
|
|
|
|
assert(q->question->n_keys > 0);
|
|
|
|
|
2015-11-24 01:25:24 +01:00
|
|
|
name = dns_question_first_name(q->question);
|
2014-07-16 22:09:00 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
|
2014-07-16 00:26:02 +02:00
|
|
|
DnsScopeMatch match;
|
|
|
|
|
2014-08-14 01:00:15 +02:00
|
|
|
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (match < 0)
|
|
|
|
return match;
|
|
|
|
|
|
|
|
if (match == DNS_SCOPE_NO)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = match;
|
|
|
|
|
|
|
|
if (match == DNS_SCOPE_YES) {
|
|
|
|
first = s;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
assert(match == DNS_SCOPE_MAYBE);
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
first = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-14 13:17:05 +02:00
|
|
|
if (found == DNS_SCOPE_NO) {
|
|
|
|
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
|
|
|
|
|
|
|
dns_query_synthesize_reply(q, &state);
|
2015-08-24 23:44:33 +02:00
|
|
|
dns_query_complete(q, state);
|
|
|
|
return 1;
|
2015-08-14 13:17:05 +02:00
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
r = dns_query_add_transaction_split(q, first);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (r < 0)
|
2014-07-31 17:46:40 +02:00
|
|
|
goto fail;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(scopes, s, first->scopes_next) {
|
|
|
|
DnsScopeMatch match;
|
|
|
|
|
2014-08-14 01:00:15 +02:00
|
|
|
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (match < 0)
|
2014-07-31 17:46:40 +02:00
|
|
|
goto fail;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
if (match != found)
|
|
|
|
continue;
|
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
r = dns_query_add_transaction_split(q, s);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (r < 0)
|
2014-07-31 17:46:40 +02:00
|
|
|
goto fail;
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
q->answer = dns_answer_unref(q->answer);
|
|
|
|
q->answer_rcode = 0;
|
2014-08-14 01:00:15 +02:00
|
|
|
q->answer_family = AF_UNSPEC;
|
|
|
|
q->answer_protocol = _DNS_PROTOCOL_INVALID;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-08-01 00:55:51 +02:00
|
|
|
r = sd_event_add_time(
|
|
|
|
q->manager->event,
|
|
|
|
&q->timeout_event_source,
|
|
|
|
clock_boottime_or_monotonic(),
|
|
|
|
now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0,
|
|
|
|
on_query_timeout, q);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
q->state = DNS_TRANSACTION_PENDING;
|
2014-07-22 21:48:41 +02:00
|
|
|
q->block_ready++;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
/* Start the transactions that are not started yet */
|
2014-07-22 21:48:41 +02:00
|
|
|
SET_FOREACH(t, q->transactions, i) {
|
2014-07-31 17:46:40 +02:00
|
|
|
if (t->state != DNS_TRANSACTION_NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = dns_transaction_go(t);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
q->block_ready--;
|
|
|
|
dns_query_ready(q);
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-16 22:09:00 +02:00
|
|
|
return 1;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
fail:
|
2014-07-16 22:09:00 +02:00
|
|
|
dns_query_stop(q);
|
2014-07-16 00:26:02 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
void dns_query_ready(DnsQuery *q) {
|
2014-07-31 17:46:40 +02:00
|
|
|
DnsTransaction *t;
|
|
|
|
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
2014-07-23 01:59:36 +02:00
|
|
|
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
|
|
|
int rcode = 0;
|
|
|
|
DnsScope *scope = NULL;
|
2014-07-30 20:39:52 +02:00
|
|
|
bool pending = false;
|
2014-07-22 21:48:41 +02:00
|
|
|
Iterator i;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
assert(q);
|
2014-07-31 17:46:40 +02:00
|
|
|
assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
/* Note that this call might invalidate the query. Callers
|
|
|
|
* should hence not attempt to access the query or transaction
|
2014-07-22 21:48:41 +02:00
|
|
|
* after calling this function, unless the block_ready
|
2014-07-17 19:38:37 +02:00
|
|
|
* counter was explicitly bumped before doing so. */
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
if (q->block_ready > 0)
|
2014-07-16 00:26:02 +02:00
|
|
|
return;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
SET_FOREACH(t, q->transactions, i) {
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
/* If we found a successful answer, ignore all answers from other scopes */
|
2014-07-31 17:46:40 +02:00
|
|
|
if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
|
2014-07-23 01:59:36 +02:00
|
|
|
continue;
|
|
|
|
|
2014-07-30 20:39:52 +02:00
|
|
|
/* One of the transactions is still going on, let's maybe wait for it */
|
2014-07-31 17:46:40 +02:00
|
|
|
if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
|
2014-07-30 20:39:52 +02:00
|
|
|
pending = true;
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
/* One of the transactions is successful, let's use
|
|
|
|
* it, and copy its data out */
|
2014-07-31 17:46:40 +02:00
|
|
|
if (t->state == DNS_TRANSACTION_SUCCESS) {
|
2014-07-23 01:59:36 +02:00
|
|
|
DnsAnswer *a;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
if (t->received) {
|
2014-07-23 01:59:36 +02:00
|
|
|
rcode = DNS_PACKET_RCODE(t->received);
|
|
|
|
a = t->received->answer;
|
2014-07-22 21:48:41 +02:00
|
|
|
} else {
|
2014-07-23 01:59:36 +02:00
|
|
|
rcode = t->cached_rcode;
|
|
|
|
a = t->cached;
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
if (state == DNS_TRANSACTION_SUCCESS) {
|
2014-07-23 01:59:36 +02:00
|
|
|
DnsAnswer *merged;
|
|
|
|
|
|
|
|
merged = dns_answer_merge(answer, a);
|
|
|
|
if (!merged) {
|
2014-07-31 17:46:40 +02:00
|
|
|
dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
|
2014-07-23 01:59:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_answer_unref(answer);
|
|
|
|
answer = merged;
|
|
|
|
} else {
|
|
|
|
dns_answer_unref(answer);
|
|
|
|
answer = dns_answer_ref(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
scope = t->scope;
|
2014-07-31 17:46:40 +02:00
|
|
|
state = DNS_TRANSACTION_SUCCESS;
|
2014-07-23 01:59:36 +02:00
|
|
|
continue;
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
|
2014-07-16 20:15:47 +02:00
|
|
|
/* One of the transactions has failed, let's see
|
|
|
|
* whether we find anything better, but if not, return
|
2014-07-23 01:59:36 +02:00
|
|
|
* its response data */
|
2014-07-31 17:46:40 +02:00
|
|
|
if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
|
2014-07-23 01:59:36 +02:00
|
|
|
DnsAnswer *a;
|
|
|
|
|
2014-07-23 00:57:25 +02:00
|
|
|
if (t->received) {
|
2014-07-23 01:59:36 +02:00
|
|
|
rcode = DNS_PACKET_RCODE(t->received);
|
|
|
|
a = t->received->answer;
|
2014-07-23 00:57:25 +02:00
|
|
|
} else {
|
2014-07-23 01:59:36 +02:00
|
|
|
rcode = t->cached_rcode;
|
|
|
|
a = t->cached;
|
2014-07-23 00:57:25 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 01:59:36 +02:00
|
|
|
dns_answer_unref(answer);
|
|
|
|
answer = dns_answer_ref(a);
|
|
|
|
|
|
|
|
scope = t->scope;
|
2014-07-31 17:46:40 +02:00
|
|
|
state = DNS_TRANSACTION_FAILURE;
|
2014-07-16 20:15:47 +02:00
|
|
|
continue;
|
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
|
2014-07-16 00:26:02 +02:00
|
|
|
state = t->state;
|
|
|
|
}
|
|
|
|
|
2014-07-30 20:39:52 +02:00
|
|
|
if (pending) {
|
|
|
|
|
|
|
|
/* If so far we weren't successful, and there's
|
|
|
|
* something still pending, then wait for it */
|
2014-07-31 17:46:40 +02:00
|
|
|
if (state != DNS_TRANSACTION_SUCCESS)
|
2014-07-30 20:39:52 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* If we already were successful, then only wait for
|
|
|
|
* other transactions on the same scope to finish. */
|
|
|
|
SET_FOREACH(t, q->transactions, i) {
|
2014-07-31 17:46:40 +02:00
|
|
|
if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
|
2014-07-30 20:39:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-31 17:46:40 +02:00
|
|
|
if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
|
2014-07-23 01:59:36 +02:00
|
|
|
q->answer = dns_answer_ref(answer);
|
|
|
|
q->answer_rcode = rcode;
|
2014-08-14 01:00:15 +02:00
|
|
|
q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
|
|
|
|
q->answer_family = scope ? scope->family : AF_UNSPEC;
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-08-14 13:17:05 +02:00
|
|
|
/* Try to synthesize a reply if we couldn't resolve something. */
|
|
|
|
dns_query_synthesize_reply(q, &state);
|
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
dns_query_complete(q, state);
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
2014-07-16 22:09:00 +02:00
|
|
|
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
|
2014-07-22 21:48:41 +02:00
|
|
|
_cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
|
|
|
|
int r;
|
2014-07-16 22:09:00 +02:00
|
|
|
|
|
|
|
assert(q);
|
|
|
|
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
q->n_cname_redirects ++;
|
2014-07-22 21:48:41 +02:00
|
|
|
if (q->n_cname_redirects > CNAME_MAX)
|
2014-07-16 22:09:00 +02:00
|
|
|
return -ELOOP;
|
|
|
|
|
2015-09-04 01:56:23 +02:00
|
|
|
r = dns_question_cname_redirect(q->question, cname, &nq);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2014-07-16 22:09:00 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
dns_question_unref(q->question);
|
|
|
|
q->question = nq;
|
|
|
|
nq = NULL;
|
2014-07-16 22:09:00 +02:00
|
|
|
|
2014-07-17 19:38:37 +02:00
|
|
|
dns_query_stop(q);
|
2014-07-31 17:46:40 +02:00
|
|
|
q->state = DNS_TRANSACTION_NULL;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-16 22:09:00 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-08-06 16:32:55 +02:00
|
|
|
|
resolved: add ResolveService() bus call for resolving SRV and DNS-SD services
This also adds client-side support for this to systemd-resolve-host.
Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.
This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.
In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.
Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
2015-11-23 21:25:40 +01:00
|
|
|
int dns_query_process_cname(DnsQuery *q) {
|
|
|
|
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
|
|
|
|
DnsResourceRecord *rr;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
|
|
|
|
if (q->state != DNS_TRANSACTION_SUCCESS)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
DNS_ANSWER_FOREACH(rr, q->answer) {
|
|
|
|
|
|
|
|
r = dns_question_matches_rr(q->question, rr);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0)
|
|
|
|
return 0; /* The answer matches directly, no need to follow cnames */
|
|
|
|
|
|
|
|
r = dns_question_matches_cname(q->question, rr);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0 && !cname)
|
|
|
|
cname = dns_resource_record_ref(rr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cname)
|
|
|
|
return 0; /* No cname to follow */
|
|
|
|
|
|
|
|
if (q->flags & SD_RESOLVED_NO_CNAME)
|
|
|
|
return -ELOOP;
|
|
|
|
|
|
|
|
/* OK, let's actually follow the CNAME */
|
|
|
|
r = dns_query_cname_redirect(q, cname);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/* Let's see if the answer can already answer the new
|
|
|
|
* redirected question */
|
|
|
|
DNS_ANSWER_FOREACH(rr, q->answer) {
|
|
|
|
r = dns_question_matches_rr(q->question, rr);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0)
|
|
|
|
return 0; /* It can answer it, yay! */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, it cannot, let's begin with the new query */
|
|
|
|
r = dns_query_go(q);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1; /* We return > 0, if we restarted the query for a new cname */
|
|
|
|
}
|
|
|
|
|
2014-08-06 16:32:55 +02:00
|
|
|
static int on_bus_track(sd_bus_track *t, void *userdata) {
|
|
|
|
DnsQuery *q = userdata;
|
|
|
|
|
|
|
|
assert(t);
|
|
|
|
assert(q);
|
|
|
|
|
|
|
|
log_debug("Client of active query vanished, aborting query.");
|
|
|
|
dns_query_complete(q, DNS_TRANSACTION_ABORTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-29 19:10:09 +02:00
|
|
|
int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
|
2014-08-06 16:32:55 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (!q->bus_track) {
|
2015-04-29 19:10:09 +02:00
|
|
|
r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q);
|
2014-08-06 16:32:55 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_track_add_sender(q->bus_track, m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|