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
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
|
|
|
|
DnsQueryCandidate *c;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
assert(ret);
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(q);
|
2015-11-25 20:47:27 +01:00
|
|
|
assert(s);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
c = new0(DnsQueryCandidate, 1);
|
|
|
|
if (!c)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
c->query = q;
|
|
|
|
c->scope = s;
|
|
|
|
|
|
|
|
LIST_PREPEND(candidates_by_query, q->candidates, c);
|
|
|
|
LIST_PREPEND(candidates_by_scope, s->query_candidates, c);
|
|
|
|
|
|
|
|
*ret = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dns_query_candidate_stop(DnsQueryCandidate *c) {
|
|
|
|
DnsTransaction *t;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
assert(c);
|
|
|
|
|
|
|
|
while ((t = set_steal_first(c->transactions))) {
|
|
|
|
set_remove(t->query_candidates, c);
|
2014-07-31 17:46:40 +02:00
|
|
|
dns_transaction_gc(t);
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
|
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dns_query_candidate_stop(c);
|
|
|
|
|
|
|
|
set_free(c->transactions);
|
|
|
|
dns_search_domain_unref(c->search_domain);
|
|
|
|
|
|
|
|
if (c->query)
|
|
|
|
LIST_REMOVE(candidates_by_query, c->query->candidates, c);
|
|
|
|
|
|
|
|
if (c->scope)
|
|
|
|
LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c);
|
|
|
|
|
|
|
|
free(c);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
|
|
|
|
_cleanup_(dns_search_domain_unrefp) DnsSearchDomain *previous = NULL;
|
|
|
|
DnsSearchDomain *next = NULL;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
if (c->search_domain && c->search_domain->linked) {
|
|
|
|
next = c->search_domain->domains_next;
|
|
|
|
|
|
|
|
if (!next) {
|
|
|
|
/* We hit the last entry. Let's see if this
|
|
|
|
* was the per-link search domain list. If so,
|
|
|
|
* let's continue with the global one. */
|
|
|
|
|
|
|
|
if (c->search_domain->type == DNS_SEARCH_DOMAIN_LINK)
|
|
|
|
next = c->query->manager->search_domains;
|
|
|
|
|
|
|
|
if (!next) /* Still no item? Then we really hit the end of the list. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* If we have, start with the per-link domains */
|
|
|
|
next = dns_scope_get_search_domains(c->scope);
|
|
|
|
|
|
|
|
if (!next) /* Fall back to the global search domains */
|
|
|
|
next = c->scope->manager->search_domains;
|
|
|
|
|
|
|
|
if (!next) /* OK, there's really nothing. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_search_domain_unref(c->search_domain);
|
|
|
|
c->search_domain = dns_search_domain_ref(next);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
|
|
|
|
DnsTransaction *t;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
assert(key);
|
|
|
|
|
|
|
|
r = set_ensure_allocated(&c->transactions, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
t = dns_scope_find_transaction(c->scope, key, true);
|
|
|
|
if (!t) {
|
|
|
|
r = dns_transaction_new(&t, c->scope, key);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = set_ensure_allocated(&t->query_candidates, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
goto gc;
|
|
|
|
|
|
|
|
r = set_put(t->query_candidates, c);
|
|
|
|
if (r < 0)
|
|
|
|
goto gc;
|
|
|
|
|
|
|
|
r = set_put(c->transactions, t);
|
|
|
|
if (r < 0) {
|
|
|
|
set_remove(t->query_candidates, c);
|
|
|
|
goto gc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
gc:
|
|
|
|
dns_transaction_gc(t);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dns_query_candidate_go(DnsQueryCandidate *c) {
|
|
|
|
DnsTransaction *t;
|
|
|
|
Iterator i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
/* Start the transactions that are not started yet */
|
|
|
|
SET_FOREACH(t, c->transactions, i) {
|
|
|
|
if (t->state != DNS_TRANSACTION_NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = dns_transaction_go(t);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
|
|
|
|
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
|
|
|
DnsTransaction *t;
|
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
if (c->error_code != 0)
|
|
|
|
return DNS_TRANSACTION_RESOURCES;
|
|
|
|
|
|
|
|
SET_FOREACH(t, c->transactions, i) {
|
|
|
|
|
|
|
|
switch (t->state) {
|
|
|
|
|
|
|
|
case DNS_TRANSACTION_PENDING:
|
|
|
|
case DNS_TRANSACTION_NULL:
|
|
|
|
return t->state;
|
|
|
|
|
|
|
|
case DNS_TRANSACTION_SUCCESS:
|
|
|
|
state = t->state;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (state != DNS_TRANSACTION_SUCCESS)
|
|
|
|
state = t->state;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
|
|
|
|
DnsResourceKey *key;
|
|
|
|
int n = 0, r;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
dns_query_candidate_stop(c);
|
|
|
|
|
|
|
|
/* Create one transaction per question key */
|
|
|
|
DNS_QUESTION_FOREACH(key, c->query->question) {
|
|
|
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
|
|
|
|
|
|
|
|
if (c->search_domain) {
|
|
|
|
r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = dns_query_candidate_add_transaction(c, new_key ?: key);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
dns_query_candidate_stop(c);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dns_query_candidate_ready(DnsQueryCandidate *c) {
|
|
|
|
DnsTransactionState state;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
state = dns_query_candidate_state(c);
|
|
|
|
|
|
|
|
if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
|
|
|
|
|
|
|
|
r = dns_query_candidate_next_search_domain(c);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (r > 0) {
|
|
|
|
/* OK, there's another search domain to try, let's do so. */
|
|
|
|
|
|
|
|
r = dns_query_candidate_setup_transactions(c);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (r > 0) {
|
|
|
|
/* New transactions where queued. Start them and wait */
|
|
|
|
|
|
|
|
r = dns_query_candidate_go(c);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_query_ready(c->query);
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
log_warning_errno(r, "Failed to follow search domains: %m");
|
|
|
|
c->error_code = r;
|
|
|
|
dns_query_ready(c->query);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dns_query_stop(DnsQuery *q) {
|
|
|
|
DnsQueryCandidate *c;
|
|
|
|
|
|
|
|
assert(q);
|
|
|
|
|
|
|
|
q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
|
|
|
|
|
|
|
|
LIST_FOREACH(candidates_by_query, c, q->candidates)
|
|
|
|
dns_query_candidate_stop(c);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
while (q->candidates)
|
|
|
|
dns_query_candidate_free(q->candidates);
|
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);
|
2015-11-25 20:47:27 +01:00
|
|
|
dns_search_domain_unref(q->answer_search_domain);
|
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;
|
2015-11-25 20:47:27 +01:00
|
|
|
q->answer_family = AF_UNSPEC;
|
|
|
|
q->answer_protocol = _DNS_PROTOCOL_INVALID;
|
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;
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
|
|
|
|
DnsQueryCandidate *c;
|
2014-07-22 21:48:41 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(q);
|
2014-07-31 17:46:40 +02:00
|
|
|
assert(s);
|
2014-07-22 21:48:41 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_query_candidate_new(&c, q, s);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
/* If this a single-label domain on DNS, we might append a suitable search domain first. */
|
|
|
|
r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r < 0)
|
2015-11-25 20:47:27 +01:00
|
|
|
goto fail;
|
|
|
|
if (r > 0) {
|
|
|
|
/* OK, we need a search domain now. Let's find one for this scope */
|
2014-07-22 21:48:41 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_query_candidate_next_search_domain(c);
|
|
|
|
if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
|
|
|
|
goto fail;
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_query_candidate_setup_transactions(c);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
return 0;
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
fail:
|
|
|
|
dns_query_candidate_free(c);
|
2014-07-22 21:48:41 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
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_rcode = DNS_RCODE_SUCCESS;
|
2015-11-25 20:47:27 +01:00
|
|
|
q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
|
|
|
|
q->answer_family = SYNTHESIZE_FAMILY(q->flags);
|
2015-08-14 13:17:05 +02:00
|
|
|
|
|
|
|
*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;
|
2015-11-25 20:47:27 +01:00
|
|
|
DnsQueryCandidate *c;
|
2014-07-22 21:48:41 +02:00
|
|
|
const char *name;
|
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
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_query_add_candidate(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;
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_query_add_candidate(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
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
/* Start the transactions */
|
|
|
|
LIST_FOREACH(candidates_by_query, c, q->candidates) {
|
|
|
|
r = dns_query_candidate_go(c);
|
|
|
|
if (r < 0) {
|
|
|
|
q->block_ready--;
|
2014-07-31 17:46:40 +02:00
|
|
|
goto fail;
|
2015-11-25 20:47:27 +01:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
2014-07-31 17:46:40 +02:00
|
|
|
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
2015-11-25 20:47:27 +01:00
|
|
|
DnsTransaction *t;
|
2014-07-22 21:48:41 +02:00
|
|
|
Iterator i;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
|
|
|
assert(q);
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
if (!c) {
|
|
|
|
dns_query_synthesize_reply(q, &state);
|
|
|
|
dns_query_complete(q, state);
|
2014-07-16 00:26:02 +02:00
|
|
|
return;
|
2015-11-25 20:47:27 +01:00
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
SET_FOREACH(t, c->transactions, i) {
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
switch (t->state) {
|
2014-07-23 01:59:36 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
case DNS_TRANSACTION_SUCCESS: {
|
|
|
|
/* We found a successfuly reply, merge it into the answer */
|
2015-11-26 22:51:35 +01:00
|
|
|
DnsAnswer *merged;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2015-11-26 22:51:35 +01:00
|
|
|
merged = dns_answer_merge(q->answer, t->answer);
|
2015-11-25 20:47:27 +01:00
|
|
|
if (!merged) {
|
|
|
|
dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_answer_unref(q->answer);
|
|
|
|
q->answer = merged;
|
2015-11-26 22:51:35 +01:00
|
|
|
q->answer_rcode = t->answer_rcode;
|
2015-11-25 20:47:27 +01:00
|
|
|
|
|
|
|
state = DNS_TRANSACTION_SUCCESS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DNS_TRANSACTION_PENDING:
|
|
|
|
case DNS_TRANSACTION_NULL:
|
|
|
|
case DNS_TRANSACTION_ABORTED:
|
|
|
|
/* Ignore transactions that didn't complete */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Any kind of failure? Store the data away,
|
|
|
|
* if there's nothing stored yet. */
|
2014-07-23 01:59:36 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
if (state != DNS_TRANSACTION_SUCCESS) {
|
|
|
|
|
|
|
|
dns_answer_unref(q->answer);
|
2015-11-26 22:51:35 +01:00
|
|
|
q->answer = dns_answer_ref(t->answer);
|
|
|
|
q->answer_rcode = t->answer_rcode;
|
2014-07-23 01:59:36 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
state = t->state;
|
2014-07-23 01:59:36 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
break;
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
2015-11-25 20:47:27 +01:00
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
q->answer_protocol = c->scope->protocol;
|
|
|
|
q->answer_family = c->scope->family;
|
2014-07-23 01:59:36 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
dns_search_domain_unref(q->answer_search_domain);
|
|
|
|
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
|
2014-07-23 00:57:25 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
dns_query_synthesize_reply(q, &state);
|
|
|
|
dns_query_complete(q, state);
|
|
|
|
}
|
2014-07-23 01:59:36 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
void dns_query_ready(DnsQuery *q) {
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
DnsQueryCandidate *bad = NULL, *c;
|
|
|
|
bool pending = false;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
assert(q);
|
|
|
|
assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
|
2014-07-30 20:39:52 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
/* Note that this call might invalidate the query. Callers
|
|
|
|
* should hence not attempt to access the query or transaction
|
|
|
|
* after calling this function, unless the block_ready
|
|
|
|
* counter was explicitly bumped before doing so. */
|
|
|
|
|
|
|
|
if (q->block_ready > 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
LIST_FOREACH(candidates_by_query, c, q->candidates) {
|
|
|
|
DnsTransactionState state;
|
|
|
|
|
|
|
|
state = dns_query_candidate_state(c);
|
|
|
|
switch (state) {
|
|
|
|
|
|
|
|
case DNS_TRANSACTION_SUCCESS:
|
|
|
|
/* One of the transactions is successful,
|
|
|
|
* let's use it, and copy its data out */
|
|
|
|
dns_query_accept(q, c);
|
2014-07-30 20:39:52 +02:00
|
|
|
return;
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
case DNS_TRANSACTION_PENDING:
|
|
|
|
case DNS_TRANSACTION_NULL:
|
|
|
|
/* One of the transactions is still going on, let's maybe wait for it */
|
|
|
|
pending = true;
|
|
|
|
break;
|
2014-07-30 20:39:52 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
default:
|
|
|
|
/* Any kind of failure */
|
|
|
|
bad = c;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
if (pending)
|
|
|
|
return;
|
2015-08-14 13:17:05 +02:00
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
dns_query_accept(q, bad);
|
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) {
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
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
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0)
|
|
|
|
return 0; /* The answer matches directly, no need to follow cnames */
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
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
|
|
|
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) {
|
2015-11-25 20:47:27 +01:00
|
|
|
r = dns_question_matches_rr(q->question, rr, 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
|
|
|
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;
|
|
|
|
}
|