mirror of
https://github.com/systemd/systemd.git
synced 2024-12-27 07:22:31 +03:00
Merge pull request #2031 from poettering/resolved-search-domains
resolved. Fully implement search domains for single-label names
This commit is contained in:
commit
c283267467
@ -5140,6 +5140,8 @@ systemd_resolved_SOURCES = \
|
||||
src/resolve/resolved-manager.h \
|
||||
src/resolve/resolved-conf.c \
|
||||
src/resolve/resolved-conf.h \
|
||||
src/resolve/resolved-resolv-conf.c \
|
||||
src/resolve/resolved-resolv-conf.h \
|
||||
src/resolve/resolved-bus.c \
|
||||
src/resolve/resolved-bus.h \
|
||||
src/resolve/resolved-link.h \
|
||||
@ -5163,6 +5165,8 @@ systemd_resolved_SOURCES = \
|
||||
src/resolve/resolved-dns-scope.c \
|
||||
src/resolve/resolved-dns-server.h \
|
||||
src/resolve/resolved-dns-server.c \
|
||||
src/resolve/resolved-dns-search-domain.h \
|
||||
src/resolve/resolved-dns-search-domain.c \
|
||||
src/resolve/resolved-dns-cache.h \
|
||||
src/resolve/resolved-dns-cache.c \
|
||||
src/resolve/resolved-dns-zone.h \
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
@ -77,10 +77,11 @@
|
||||
sent to one of the listed DNS servers in parallel to any
|
||||
per-interface DNS servers acquired from
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
For compatibility reasons, if set to the empty list, the DNS
|
||||
servers listed in <filename>/etc/resolv.conf</filename> are
|
||||
used, if any are configured there. This setting defaults to
|
||||
the empty list.</para></listitem>
|
||||
For compatibility reasons, if this setting is not specified,
|
||||
the DNS servers listed in
|
||||
<filename>/etc/resolv.conf</filename> are used instead, if
|
||||
that file exists and any servers are configured in it. This
|
||||
setting defaults to the empty list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -97,6 +98,16 @@
|
||||
instead.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Domains=</varname></term>
|
||||
<listitem><para>A space-separated list of search domains. For
|
||||
compatibility reasons, if this setting is not specified, the
|
||||
search domains listed in <filename>/etc/resolv.conf</filename>
|
||||
are used instead, if that file exists and any domains are
|
||||
configured in it. This setting defaults to the empty
|
||||
list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LLMNR=</varname></term>
|
||||
<listitem><para>Takes a boolean argument or
|
||||
|
@ -29,6 +29,17 @@ static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
|
||||
return (OrderedSet*) ordered_hashmap_new(ops);
|
||||
}
|
||||
|
||||
static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) {
|
||||
if (*s)
|
||||
return 0;
|
||||
|
||||
*s = ordered_set_new(ops);
|
||||
if (!*s)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
|
||||
ordered_hashmap_free((OrderedHashmap*) s);
|
||||
return NULL;
|
||||
|
@ -27,7 +27,6 @@
|
||||
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
|
||||
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
|
||||
static inline Set *set_free(Set *s) {
|
||||
internal_hashmap_free(HASHMAP_BASE(s));
|
||||
return NULL;
|
||||
|
@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
|
||||
/* End of name */
|
||||
break;
|
||||
else if (c <= 63) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *label;
|
||||
|
||||
/* Literal label */
|
||||
@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
|
||||
if (pos > optlen)
|
||||
return -EMSGSIZE;
|
||||
|
||||
r = dns_label_escape(label, c, &t);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
ret[n++] = '.';
|
||||
else
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
memcpy(ret + n, t, r);
|
||||
n += r;
|
||||
continue;
|
||||
} else {
|
||||
|
@ -604,7 +604,7 @@ static int client_send_discover(sd_dhcp_client *client) {
|
||||
their messages MUST NOT also send the Host Name option". Just send
|
||||
one of the two depending on the hostname type.
|
||||
*/
|
||||
if (dns_name_single_label(client->hostname)) {
|
||||
if (dns_name_is_single_label(client->hostname)) {
|
||||
/* it is unclear from RFC 2131 if client should send hostname in
|
||||
DHCPDISCOVER but dhclient does and so we do as well
|
||||
*/
|
||||
@ -719,7 +719,7 @@ static int client_send_request(sd_dhcp_client *client) {
|
||||
}
|
||||
|
||||
if (client->hostname) {
|
||||
if (dns_name_single_label(client->hostname))
|
||||
if (dns_name_is_single_label(client->hostname))
|
||||
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
||||
DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
|
@ -680,6 +680,7 @@ static void help(void) {
|
||||
" --service-address=BOOL Do [not] resolve address for services\n"
|
||||
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
|
||||
" --cname=BOOL Do [not] follow CNAME redirects\n"
|
||||
" --search=BOOL Do [not] use search domains\n"
|
||||
" --legend=BOOL Do [not] print column headers\n"
|
||||
, program_invocation_short_name, program_invocation_short_name);
|
||||
}
|
||||
@ -692,6 +693,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CNAME,
|
||||
ARG_SERVICE_ADDRESS,
|
||||
ARG_SERVICE_TXT,
|
||||
ARG_SEARCH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -705,6 +707,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "service", no_argument, NULL, ARG_SERVICE },
|
||||
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
|
||||
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
|
||||
{ "search", required_argument, NULL, ARG_SEARCH },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -834,6 +837,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_flags &= ~SD_RESOLVED_NO_TXT;
|
||||
break;
|
||||
|
||||
case ARG_SEARCH:
|
||||
r = parse_boolean(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --search argument.");
|
||||
if (r == 0)
|
||||
arg_flags |= SD_RESOLVED_NO_SEARCH;
|
||||
else
|
||||
arg_flags &= ~SD_RESOLVED_NO_SEARCH;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -38,7 +38,7 @@ static int reply_query_state(DnsQuery *q) {
|
||||
|
||||
name = ip;
|
||||
} else
|
||||
name = dns_question_name(q->question);
|
||||
name = dns_question_first_name(q->question);
|
||||
|
||||
switch (q->state) {
|
||||
|
||||
@ -145,7 +145,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
|
||||
|
||||
r = dns_query_process_cname(q);
|
||||
if (r == -ELOOP) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
if (r < 0)
|
||||
@ -166,7 +166,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
|
||||
int ifindex;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -184,7 +184,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
|
||||
}
|
||||
|
||||
if (added <= 0) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
|
||||
if (r == 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
|
||||
|
||||
r = check_ifindex_flags(ifindex, &flags, 0, error);
|
||||
r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -318,7 +318,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
|
||||
|
||||
if (q->answer) {
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -400,7 +400,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, ifindex, flags);
|
||||
r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -474,7 +474,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
|
||||
|
||||
r = dns_query_process_cname(q);
|
||||
if (r == -ELOOP) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
if (r < 0)
|
||||
@ -495,7 +495,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
|
||||
int ifindex;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -510,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
|
||||
}
|
||||
|
||||
if (added <= 0) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -574,7 +574,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, ifindex, flags);
|
||||
r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -620,7 +620,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
|
||||
if (aux->auxiliary_result != 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(dns_question_name(aux->question), rr->srv.name);
|
||||
r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -628,7 +628,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
|
||||
|
||||
DNS_ANSWER_FOREACH(zz, aux->answer) {
|
||||
|
||||
r = dns_question_matches_rr(aux->question, zz);
|
||||
r = dns_question_matches_rr(aux->question, zz, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -672,7 +672,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
|
||||
if (aux->auxiliary_result != 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(dns_question_name(aux->question), rr->srv.name);
|
||||
r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -680,7 +680,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
|
||||
|
||||
r = dns_question_matches_rr(aux->question, zz);
|
||||
r = dns_question_matches_rr(aux->question, zz, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@ -781,7 +781,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
|
||||
assert(bad->auxiliary_result != 0);
|
||||
|
||||
if (bad->auxiliary_result == -ELOOP) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(bad->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -806,7 +806,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
|
||||
DnsResourceRecord *rr;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -826,7 +826,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
|
||||
}
|
||||
|
||||
if (added <= 0) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -842,7 +842,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
|
||||
DnsResourceRecord *rr;
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -919,7 +919,7 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(q->manager, &aux, question, ifindex, q->flags);
|
||||
r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -965,7 +965,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
|
||||
|
||||
r = dns_query_process_cname(q);
|
||||
if (r == -ELOOP) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
if (r < 0)
|
||||
@ -978,7 +978,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
|
||||
int ifindex;
|
||||
|
||||
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0)
|
||||
@ -1001,7 +1001,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
|
||||
}
|
||||
|
||||
if (found <= 0) {
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_name(q->question));
|
||||
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -1049,13 +1049,8 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
|
||||
|
||||
if (isempty(type))
|
||||
type = NULL;
|
||||
else {
|
||||
r = dns_srv_type_verify(type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
|
||||
}
|
||||
else if (!dns_srv_type_is_valid(type))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
|
||||
|
||||
r = dns_name_is_valid(domain);
|
||||
if (r < 0)
|
||||
@ -1086,7 +1081,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, ifindex, flags);
|
||||
r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1109,8 +1104,123 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int append_dns_server(sd_bus_message *reply, DnsServer *s) {
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
assert(s);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'r', "iiay");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int bus_property_get_dns_servers(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
unsigned c = 0;
|
||||
DnsServer *s;
|
||||
Iterator i;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(iiay)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(servers, s, m->dns_servers) {
|
||||
r = append_dns_server(reply, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
LIST_FOREACH(servers, s, l->dns_servers) {
|
||||
r = append_dns_server(reply, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == 0) {
|
||||
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
|
||||
r = append_dns_server(reply, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int bus_property_get_search_domains(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
DnsSearchDomain *d;
|
||||
Iterator i;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(is)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(domains, d, m->search_domains) {
|
||||
r = sd_bus_message_append(reply, "(is)", 0, d->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
LIST_FOREACH(domains, d, l->search_domains) {
|
||||
r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable resolve_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
|
||||
SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0),
|
||||
SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0),
|
||||
|
||||
SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
@ -27,53 +27,99 @@
|
||||
#include "resolved-conf.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) {
|
||||
DnsServer *first;
|
||||
int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
|
||||
union in_addr_union address;
|
||||
int family, r;
|
||||
DnsServer *s;
|
||||
|
||||
assert(m);
|
||||
assert(word);
|
||||
|
||||
r = in_addr_from_string_auto(word, &family, &address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Filter out duplicates */
|
||||
s = dns_server_find(manager_get_first_dns_server(m, type), family, &address);
|
||||
if (s) {
|
||||
/*
|
||||
* Drop the marker. This is used to find the servers
|
||||
* that ceased to exist, see
|
||||
* manager_mark_dns_servers() and
|
||||
* manager_flush_marked_dns_servers().
|
||||
*/
|
||||
dns_server_move_back_and_unmark(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dns_server_new(m, NULL, type, NULL, family, &address);
|
||||
}
|
||||
|
||||
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(string);
|
||||
|
||||
first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers;
|
||||
|
||||
for(;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
union in_addr_union addr;
|
||||
bool found = false;
|
||||
DnsServer *s;
|
||||
int family;
|
||||
|
||||
r = extract_first_word(&string, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse resolved dns server syntax \"%s\": %m", string);
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = in_addr_from_string_auto(word, &family, &addr);
|
||||
if (r < 0) {
|
||||
log_warning("Ignoring invalid DNS address '%s'", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Filter out duplicates */
|
||||
LIST_FOREACH(servers, s, first)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, &addr)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
continue;
|
||||
|
||||
r = dns_server_new(m, NULL, type, NULL, family, &addr);
|
||||
r = manager_add_dns_server_by_string(m, type, word);
|
||||
if (r < 0)
|
||||
return r;
|
||||
log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dnsv(
|
||||
int manager_add_search_domain_by_string(Manager *m, const char *domain) {
|
||||
DnsSearchDomain *d;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(domain);
|
||||
|
||||
r = dns_search_domain_find(m->search_domains, domain, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
dns_search_domain_move_back_and_unmark(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
|
||||
}
|
||||
|
||||
int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(string);
|
||||
|
||||
for(;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = manager_add_search_domain_by_string(m, word);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dns_servers(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
@ -95,10 +141,10 @@ int config_parse_dnsv(
|
||||
|
||||
if (isempty(rvalue))
|
||||
/* Empty assignment means clear the list */
|
||||
manager_flush_dns_servers(m, ltype);
|
||||
dns_server_unlink_all(manager_get_first_dns_server(m, ltype));
|
||||
else {
|
||||
/* Otherwise, add to the list */
|
||||
r = manager_parse_dns_server(m, ltype, rvalue);
|
||||
r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue);
|
||||
return 0;
|
||||
@ -109,6 +155,47 @@ int config_parse_dnsv(
|
||||
* /etc/resolv.conf */
|
||||
if (ltype == DNS_SERVER_SYSTEM)
|
||||
m->read_resolv_conf = false;
|
||||
if (ltype == DNS_SERVER_FALLBACK)
|
||||
m->need_builtin_fallbacks = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_search_domains(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(m);
|
||||
|
||||
if (isempty(rvalue))
|
||||
/* Empty assignment means clear the list */
|
||||
dns_search_domain_unlink_all(m->search_domains);
|
||||
else {
|
||||
/* Otherwise, add to the list */
|
||||
r = manager_parse_search_domains_and_warn(m, rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have a manual setting, then we stop reading
|
||||
* /etc/resolv.conf */
|
||||
m->read_resolv_conf = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -148,11 +235,24 @@ int config_parse_support(
|
||||
}
|
||||
|
||||
int manager_parse_config_file(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
return config_parse_many(PKGSYSCONFDIR "/resolved.conf",
|
||||
CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
|
||||
"Resolve\0",
|
||||
config_item_perf_lookup, resolved_gperf_lookup,
|
||||
false, m);
|
||||
r = config_parse_many(PKGSYSCONFDIR "/resolved.conf",
|
||||
CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
|
||||
"Resolve\0",
|
||||
config_item_perf_lookup, resolved_gperf_lookup,
|
||||
false, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->need_builtin_fallbacks) {
|
||||
r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -23,10 +23,16 @@
|
||||
|
||||
#include "resolved-manager.h"
|
||||
|
||||
int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string);
|
||||
int manager_parse_config_file(Manager *m);
|
||||
|
||||
int manager_add_search_domain_by_string(Manager *m, const char *domain);
|
||||
int manager_parse_search_domains_and_warn(Manager *m, const char *string);
|
||||
|
||||
int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
|
||||
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
|
||||
|
||||
const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
|
||||
|
||||
int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define SD_RESOLVED_NO_CNAME ((uint64_t) 8)
|
||||
#define SD_RESOLVED_NO_TXT ((uint64_t) 16)
|
||||
#define SD_RESOLVED_NO_ADDRESS ((uint64_t) 32)
|
||||
#define SD_RESOLVED_NO_SEARCH ((uint64_t) 64)
|
||||
|
||||
#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
|
||||
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
|
||||
|
@ -141,7 +141,7 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < a->n_rrs; i++) {
|
||||
r = dns_resource_key_match_rr(key, a->items[i].rr);
|
||||
r = dns_resource_key_match_rr(key, a->items[i].rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
|
@ -61,7 +61,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
|
||||
|
||||
#define DNS_ANSWER_FOREACH(kk, a) \
|
||||
for (unsigned _i = ({ \
|
||||
(kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \
|
||||
(kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
|
||||
0; \
|
||||
}); \
|
||||
(a) && ((_i) < (a)->n_rrs); \
|
||||
@ -69,9 +69,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
|
||||
|
||||
#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) \
|
||||
for (unsigned _i = ({ \
|
||||
(kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \
|
||||
(ifindex) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].ifindex : 0); \
|
||||
(kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
|
||||
(ifindex) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
|
||||
0; \
|
||||
}); \
|
||||
(a) && ((_i) < (a)->n_rrs); \
|
||||
_i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL), (ifindex) = (_i < (a)->n_rrs ? (a)->items[_i].ifindex : 0))
|
||||
_i++, (kk) = ((_i < (a)->n_rrs) ? (a)->items[_i].rr : NULL), (ifindex) = ((_i < (a)->n_rrs) ? (a)->items[_i].ifindex : 0))
|
||||
|
@ -1145,7 +1145,6 @@ int dns_packet_read_name(
|
||||
/* End of name */
|
||||
break;
|
||||
else if (c <= 63) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *label;
|
||||
|
||||
/* Literal label */
|
||||
@ -1153,21 +1152,20 @@ int dns_packet_read_name(
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = dns_label_escape(label, c, &t);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
ret[n++] = '.';
|
||||
else
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
memcpy(ret + n, t, r);
|
||||
n += r;
|
||||
continue;
|
||||
} else if (allow_compression && (c & 0xc0) == 0xc0) {
|
||||
|
@ -32,17 +32,275 @@
|
||||
#define QUERIES_MAX 2048
|
||||
#define AUXILIARY_QUERIES_MAX 64
|
||||
|
||||
static void dns_query_stop(DnsQuery *q) {
|
||||
static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) {
|
||||
DnsQueryCandidate *c;
|
||||
|
||||
assert(ret);
|
||||
assert(q);
|
||||
assert(s);
|
||||
|
||||
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;
|
||||
|
||||
assert(c);
|
||||
|
||||
while ((t = set_steal_first(c->transactions))) {
|
||||
set_remove(t->query_candidates, c);
|
||||
dns_transaction_gc(t);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
while ((t = set_steal_first(q->transactions))) {
|
||||
set_remove(t->queries, q);
|
||||
dns_transaction_gc(t);
|
||||
}
|
||||
LIST_FOREACH(candidates_by_query, c, q->candidates)
|
||||
dns_query_candidate_stop(c);
|
||||
}
|
||||
|
||||
DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
@ -58,11 +316,12 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
|
||||
}
|
||||
|
||||
dns_query_stop(q);
|
||||
set_free(q->transactions);
|
||||
while (q->candidates)
|
||||
dns_query_candidate_free(q->candidates);
|
||||
|
||||
dns_question_unref(q->question);
|
||||
dns_answer_unref(q->answer);
|
||||
dns_search_domain_unref(q->answer_search_domain);
|
||||
|
||||
sd_bus_message_unref(q->request);
|
||||
sd_bus_track_unref(q->bus_track);
|
||||
@ -85,7 +344,7 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
|
||||
assert(m);
|
||||
assert(question);
|
||||
|
||||
r = dns_question_is_valid(question);
|
||||
r = dns_question_is_valid_for_query(question);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -99,6 +358,8 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
|
||||
q->question = dns_question_ref(question);
|
||||
q->ifindex = ifindex;
|
||||
q->flags = flags;
|
||||
q->answer_family = AF_UNSPEC;
|
||||
q->answer_protocol = _DNS_PROTOCOL_INVALID;
|
||||
|
||||
for (i = 0; i < question->n_keys; i++) {
|
||||
_cleanup_free_ char *p;
|
||||
@ -170,64 +431,40 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
|
||||
DnsTransaction *t;
|
||||
static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
|
||||
DnsQueryCandidate *c;
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
assert(s);
|
||||
assert(key);
|
||||
|
||||
r = set_ensure_allocated(&q->transactions, NULL);
|
||||
r = dns_query_candidate_new(&c, q, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = dns_scope_find_transaction(s, key, true);
|
||||
if (!t) {
|
||||
r = dns_transaction_new(&t, s, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* 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));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r > 0) {
|
||||
/* OK, we need a search domain now. Let's find one for this scope */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
r = set_ensure_allocated(&t->queries, NULL);
|
||||
r = dns_query_candidate_setup_transactions(c);
|
||||
if (r < 0)
|
||||
goto gc;
|
||||
|
||||
r = set_put(t->queries, q);
|
||||
if (r < 0)
|
||||
goto gc;
|
||||
|
||||
r = set_put(q->transactions, t);
|
||||
if (r < 0) {
|
||||
set_remove(t->queries, q);
|
||||
goto gc;
|
||||
}
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
gc:
|
||||
dns_transaction_gc(t);
|
||||
fail:
|
||||
dns_query_candidate_free(c);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
assert(s);
|
||||
|
||||
/* 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]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SYNTHESIZE_IFINDEX(int ifindex) {
|
||||
|
||||
/* When the caller asked for resolving on a specific
|
||||
@ -630,9 +867,9 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
|
||||
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;
|
||||
q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
|
||||
q->answer_family = SYNTHESIZE_FAMILY(q->flags);
|
||||
|
||||
*state = DNS_TRANSACTION_SUCCESS;
|
||||
|
||||
@ -642,9 +879,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
|
||||
int dns_query_go(DnsQuery *q) {
|
||||
DnsScopeMatch found = DNS_SCOPE_NO;
|
||||
DnsScope *s, *first = NULL;
|
||||
DnsTransaction *t;
|
||||
DnsQueryCandidate *c;
|
||||
const char *name;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(q);
|
||||
@ -655,7 +891,7 @@ int dns_query_go(DnsQuery *q) {
|
||||
assert(q->question);
|
||||
assert(q->question->n_keys > 0);
|
||||
|
||||
name = dns_question_name(q->question);
|
||||
name = dns_question_first_name(q->question);
|
||||
|
||||
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
|
||||
DnsScopeMatch match;
|
||||
@ -688,7 +924,7 @@ int dns_query_go(DnsQuery *q) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = dns_query_add_transaction_split(q, first);
|
||||
r = dns_query_add_candidate(q, first);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -702,7 +938,7 @@ int dns_query_go(DnsQuery *q) {
|
||||
if (match != found)
|
||||
continue;
|
||||
|
||||
r = dns_query_add_transaction_split(q, s);
|
||||
r = dns_query_add_candidate(q, s);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
@ -724,14 +960,13 @@ int dns_query_go(DnsQuery *q) {
|
||||
q->state = DNS_TRANSACTION_PENDING;
|
||||
q->block_ready++;
|
||||
|
||||
/* Start the transactions that are not started yet */
|
||||
SET_FOREACH(t, q->transactions, i) {
|
||||
if (t->state != DNS_TRANSACTION_NULL)
|
||||
continue;
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0)
|
||||
/* Start the transactions */
|
||||
LIST_FOREACH(candidates_by_query, c, q->candidates) {
|
||||
r = dns_query_candidate_go(c);
|
||||
if (r < 0) {
|
||||
q->block_ready--;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
q->block_ready--;
|
||||
@ -744,15 +979,92 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
void dns_query_ready(DnsQuery *q) {
|
||||
DnsTransaction *t;
|
||||
static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
||||
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
int rcode = 0;
|
||||
DnsScope *scope = NULL;
|
||||
bool pending = false;
|
||||
DnsTransaction *t;
|
||||
Iterator i;
|
||||
|
||||
assert(q);
|
||||
|
||||
if (!c) {
|
||||
dns_query_synthesize_reply(q, &state);
|
||||
dns_query_complete(q, state);
|
||||
return;
|
||||
}
|
||||
|
||||
SET_FOREACH(t, c->transactions, i) {
|
||||
|
||||
switch (t->state) {
|
||||
|
||||
case DNS_TRANSACTION_SUCCESS: {
|
||||
/* We found a successfuly reply, merge it into the answer */
|
||||
DnsAnswer *merged, *a;
|
||||
|
||||
if (t->received) {
|
||||
q->answer_rcode = DNS_PACKET_RCODE(t->received);
|
||||
a = t->received->answer;
|
||||
} else {
|
||||
q->answer_rcode = t->cached_rcode;
|
||||
a = t->cached;
|
||||
}
|
||||
|
||||
merged = dns_answer_merge(q->answer, a);
|
||||
if (!merged) {
|
||||
dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
dns_answer_unref(q->answer);
|
||||
q->answer = merged;
|
||||
|
||||
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. */
|
||||
|
||||
if (state != DNS_TRANSACTION_SUCCESS) {
|
||||
|
||||
dns_answer_unref(q->answer);
|
||||
|
||||
if (t->received) {
|
||||
q->answer = dns_answer_ref(t->received->answer);
|
||||
q->answer_rcode = DNS_PACKET_RCODE(t->received);
|
||||
} else {
|
||||
q->answer = dns_answer_ref(t->cached);
|
||||
q->answer_rcode = t->cached_rcode;
|
||||
}
|
||||
|
||||
state = t->state;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
q->answer_protocol = c->scope->protocol;
|
||||
q->answer_family = c->scope->family;
|
||||
|
||||
dns_search_domain_unref(q->answer_search_domain);
|
||||
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
|
||||
|
||||
dns_query_synthesize_reply(q, &state);
|
||||
dns_query_complete(q, state);
|
||||
}
|
||||
|
||||
void dns_query_ready(DnsQuery *q) {
|
||||
|
||||
DnsQueryCandidate *bad = NULL, *c;
|
||||
bool pending = false;
|
||||
|
||||
assert(q);
|
||||
assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
|
||||
|
||||
@ -764,104 +1076,35 @@ void dns_query_ready(DnsQuery *q) {
|
||||
if (q->block_ready > 0)
|
||||
return;
|
||||
|
||||
SET_FOREACH(t, q->transactions, i) {
|
||||
LIST_FOREACH(candidates_by_query, c, q->candidates) {
|
||||
DnsTransactionState state;
|
||||
|
||||
/* If we found a successful answer, ignore all answers from other scopes */
|
||||
if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope)
|
||||
continue;
|
||||
state = dns_query_candidate_state(c);
|
||||
switch (state) {
|
||||
|
||||
/* One of the transactions is still going on, let's maybe wait for it */
|
||||
if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) {
|
||||
pending = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* One of the transactions is successful, let's use
|
||||
* it, and copy its data out */
|
||||
if (t->state == DNS_TRANSACTION_SUCCESS) {
|
||||
DnsAnswer *a;
|
||||
|
||||
if (t->received) {
|
||||
rcode = DNS_PACKET_RCODE(t->received);
|
||||
a = t->received->answer;
|
||||
} else {
|
||||
rcode = t->cached_rcode;
|
||||
a = t->cached;
|
||||
}
|
||||
|
||||
if (state == DNS_TRANSACTION_SUCCESS) {
|
||||
DnsAnswer *merged;
|
||||
|
||||
merged = dns_answer_merge(answer, a);
|
||||
if (!merged) {
|
||||
dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
dns_answer_unref(answer);
|
||||
answer = merged;
|
||||
} else {
|
||||
dns_answer_unref(answer);
|
||||
answer = dns_answer_ref(a);
|
||||
}
|
||||
|
||||
scope = t->scope;
|
||||
state = DNS_TRANSACTION_SUCCESS;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* One of the transactions has failed, let's see
|
||||
* whether we find anything better, but if not, return
|
||||
* its response data */
|
||||
if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) {
|
||||
DnsAnswer *a;
|
||||
|
||||
if (t->received) {
|
||||
rcode = DNS_PACKET_RCODE(t->received);
|
||||
a = t->received->answer;
|
||||
} else {
|
||||
rcode = t->cached_rcode;
|
||||
a = t->cached;
|
||||
}
|
||||
|
||||
dns_answer_unref(answer);
|
||||
answer = dns_answer_ref(a);
|
||||
|
||||
scope = t->scope;
|
||||
state = DNS_TRANSACTION_FAILURE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS)
|
||||
state = t->state;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
|
||||
/* If so far we weren't successful, and there's
|
||||
* something still pending, then wait for it */
|
||||
if (state != DNS_TRANSACTION_SUCCESS)
|
||||
case DNS_TRANSACTION_SUCCESS:
|
||||
/* One of the transactions is successful,
|
||||
* let's use it, and copy its data out */
|
||||
dns_query_accept(q, c);
|
||||
return;
|
||||
|
||||
/* If we already were successful, then only wait for
|
||||
* other transactions on the same scope to finish. */
|
||||
SET_FOREACH(t, q->transactions, i) {
|
||||
if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
|
||||
return;
|
||||
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;
|
||||
|
||||
default:
|
||||
/* Any kind of failure */
|
||||
bad = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) {
|
||||
q->answer = dns_answer_ref(answer);
|
||||
q->answer_rcode = rcode;
|
||||
q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID;
|
||||
q->answer_family = scope ? scope->family : AF_UNSPEC;
|
||||
}
|
||||
if (pending)
|
||||
return;
|
||||
|
||||
/* Try to synthesize a reply if we couldn't resolve something. */
|
||||
dns_query_synthesize_reply(q, &state);
|
||||
|
||||
dns_query_complete(q, state);
|
||||
dns_query_accept(q, bad);
|
||||
}
|
||||
|
||||
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
|
||||
@ -900,13 +1143,13 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, q->answer) {
|
||||
|
||||
r = dns_question_matches_rr(q->question, rr);
|
||||
r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
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);
|
||||
r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0 && !cname)
|
||||
@ -927,7 +1170,7 @@ int dns_query_process_cname(DnsQuery *q) {
|
||||
/* 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);
|
||||
r = dns_question_matches_rr(q->question, rr, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
|
@ -26,11 +26,26 @@
|
||||
|
||||
#include "set.h"
|
||||
|
||||
typedef struct DnsQueryCandidate DnsQueryCandidate;
|
||||
typedef struct DnsQuery DnsQuery;
|
||||
|
||||
#include "resolved-dns-answer.h"
|
||||
#include "resolved-dns-question.h"
|
||||
#include "resolved-dns-stream.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
|
||||
struct DnsQueryCandidate {
|
||||
DnsQuery *query;
|
||||
DnsScope *scope;
|
||||
|
||||
DnsSearchDomain *search_domain;
|
||||
|
||||
int error_code;
|
||||
Set *transactions;
|
||||
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
|
||||
};
|
||||
|
||||
struct DnsQuery {
|
||||
Manager *manager;
|
||||
@ -45,13 +60,13 @@ struct DnsQuery {
|
||||
int auxiliary_result;
|
||||
|
||||
DnsQuestion *question;
|
||||
|
||||
uint64_t flags;
|
||||
int ifindex;
|
||||
|
||||
DnsTransactionState state;
|
||||
unsigned n_cname_redirects;
|
||||
|
||||
LIST_HEAD(DnsQueryCandidate, candidates);
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
/* Discovered data */
|
||||
@ -59,6 +74,7 @@ struct DnsQuery {
|
||||
int answer_family;
|
||||
DnsProtocol answer_protocol;
|
||||
int answer_rcode;
|
||||
DnsSearchDomain *answer_search_domain;
|
||||
|
||||
/* Bus client information */
|
||||
sd_bus_message *request;
|
||||
@ -71,14 +87,15 @@ struct DnsQuery {
|
||||
void (*complete)(DnsQuery* q);
|
||||
unsigned block_ready;
|
||||
|
||||
Set *transactions;
|
||||
|
||||
sd_bus_track *bus_track;
|
||||
|
||||
LIST_FIELDS(DnsQuery, queries);
|
||||
LIST_FIELDS(DnsQuery, auxiliary_queries);
|
||||
};
|
||||
|
||||
DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
|
||||
void dns_query_candidate_ready(DnsQueryCandidate *c);
|
||||
|
||||
int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
|
||||
DnsQuery *dns_query_free(DnsQuery *q);
|
||||
|
||||
|
@ -89,7 +89,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
@ -99,7 +99,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < q->n_keys; i++) {
|
||||
r = dns_resource_key_match_rr(q->keys[i], rr);
|
||||
r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
@ -107,7 +107,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
@ -117,7 +117,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < q->n_keys; i++) {
|
||||
r = dns_resource_key_match_cname(q->keys[i], rr);
|
||||
r = dns_resource_key_match_cname(q->keys[i], rr, search_domain);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
@ -125,7 +125,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_question_is_valid(DnsQuestion *q) {
|
||||
int dns_question_is_valid_for_query(DnsQuestion *q) {
|
||||
const char *name;
|
||||
unsigned i;
|
||||
int r;
|
||||
@ -274,8 +274,10 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *dns_question_name(DnsQuestion *q) {
|
||||
assert(q);
|
||||
const char *dns_question_first_name(DnsQuestion *q) {
|
||||
|
||||
if (!q)
|
||||
return NULL;
|
||||
|
||||
if (q->n_keys < 1)
|
||||
return NULL;
|
||||
|
@ -25,7 +25,7 @@ typedef struct DnsQuestion DnsQuestion;
|
||||
|
||||
#include "resolved-dns-rr.h"
|
||||
|
||||
/* A simple array of resources keys, all sharing the same domain */
|
||||
/* A simple array of resources keys */
|
||||
|
||||
struct DnsQuestion {
|
||||
unsigned n_ref;
|
||||
@ -43,14 +43,22 @@ int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt)
|
||||
|
||||
int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
|
||||
|
||||
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
|
||||
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
|
||||
int dns_question_is_valid(DnsQuestion *q);
|
||||
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
|
||||
int dns_question_is_valid_for_query(DnsQuestion *q);
|
||||
int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
|
||||
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
|
||||
|
||||
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
|
||||
|
||||
const char *dns_question_name(DnsQuestion *q);
|
||||
const char *dns_question_first_name(DnsQuestion *q);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
|
||||
|
||||
#define DNS_QUESTION_FOREACH(key, q) \
|
||||
for (unsigned _i = ({ \
|
||||
(key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \
|
||||
0; \
|
||||
}); \
|
||||
(q) && ((_i) < (q)->n_keys); \
|
||||
_i++, (key) = (_i < (q)->n_keys ? (q)->keys[_i] : NULL))
|
||||
|
@ -86,6 +86,34 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D
|
||||
}
|
||||
}
|
||||
|
||||
int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
|
||||
DnsResourceKey *new_key;
|
||||
char *joined;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(key);
|
||||
assert(name);
|
||||
|
||||
if (dns_name_is_root(name)) {
|
||||
*ret = dns_resource_key_ref(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
new_key = dns_resource_key_new_consume(key->class, key->type, joined);
|
||||
if (!new_key) {
|
||||
free(joined);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = new_key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
|
||||
DnsResourceKey *k;
|
||||
|
||||
@ -145,20 +173,42 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) {
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
assert(rr);
|
||||
|
||||
/* Checks if an rr matches the specified key. If a search
|
||||
* domain is specified, it will also be checked if the key
|
||||
* with the search domain suffixed might match the RR. */
|
||||
|
||||
if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
|
||||
return 0;
|
||||
|
||||
if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
|
||||
return 0;
|
||||
|
||||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (search_domain) {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) {
|
||||
int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
assert(rr);
|
||||
|
||||
@ -166,11 +216,30 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec
|
||||
return 0;
|
||||
|
||||
if (rr->key->type == DNS_TYPE_CNAME)
|
||||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
else if (rr->key->type == DNS_TYPE_DNAME)
|
||||
return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (search_domain) {
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rr->key->type == DNS_TYPE_CNAME)
|
||||
return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
else if (rr->key->type == DNS_TYPE_DNAME)
|
||||
return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
|
||||
|
@ -188,12 +188,13 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *
|
||||
DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
|
||||
int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
|
||||
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
|
||||
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
|
||||
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr);
|
||||
int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr);
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
|
||||
|
||||
|
@ -69,18 +69,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsScope* dns_scope_free(DnsScope *s) {
|
||||
static void dns_scope_abort_transactions(DnsScope *s) {
|
||||
DnsTransaction *t;
|
||||
DnsResourceRecord *rr;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
assert(s);
|
||||
|
||||
log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
|
||||
|
||||
dns_scope_llmnr_membership(s, false);
|
||||
|
||||
while ((t = hashmap_steal_first(s->transactions))) {
|
||||
while ((t = hashmap_first(s->transactions))) {
|
||||
/* Abort the transaction, but make sure it is not
|
||||
* freed while we still look at it */
|
||||
|
||||
@ -90,6 +84,21 @@ DnsScope* dns_scope_free(DnsScope *s) {
|
||||
|
||||
dns_transaction_free(t);
|
||||
}
|
||||
}
|
||||
|
||||
DnsScope* dns_scope_free(DnsScope *s) {
|
||||
DnsResourceRecord *rr;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
|
||||
|
||||
dns_scope_llmnr_membership(s, false);
|
||||
dns_scope_abort_transactions(s);
|
||||
|
||||
while (s->query_candidates)
|
||||
dns_query_candidate_free(s->query_candidates);
|
||||
|
||||
hashmap_free(s->transactions);
|
||||
|
||||
@ -103,7 +112,6 @@ DnsScope* dns_scope_free(DnsScope *s) {
|
||||
dns_zone_flush(&s->zone);
|
||||
|
||||
LIST_REMOVE(scopes, s->manager->dns_scopes, s);
|
||||
strv_free(s->domains);
|
||||
free(s);
|
||||
|
||||
return NULL;
|
||||
@ -136,11 +144,11 @@ void dns_scope_next_dns_server(DnsScope *s) {
|
||||
void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
|
||||
assert(s);
|
||||
|
||||
if (rtt > s->max_rtt) {
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2),
|
||||
MULTICAST_RESEND_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
if (rtt <= s->max_rtt)
|
||||
return;
|
||||
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
|
||||
@ -323,7 +331,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
|
||||
}
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
|
||||
char **i;
|
||||
DnsSearchDomain *d;
|
||||
|
||||
assert(s);
|
||||
assert(domain);
|
||||
@ -334,7 +342,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
||||
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
if (dns_name_root(domain) != 0)
|
||||
if (dns_name_is_root(domain))
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Never resolve any loopback hostname or IP address via DNS,
|
||||
@ -345,15 +353,22 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
||||
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
STRV_FOREACH(i, s->domains)
|
||||
if (dns_name_endswith(domain, *i) > 0)
|
||||
/* Always honour search domains for routing queries. Note that
|
||||
* we return DNS_SCOPE_YES here, rather than just
|
||||
* DNS_SCOPE_MAYBE, which means wildcard scopes won't be
|
||||
* considered anymore. */
|
||||
LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
|
||||
if (dns_name_endswith(domain, d->name) > 0)
|
||||
return DNS_SCOPE_YES;
|
||||
|
||||
switch (s->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
|
||||
dns_name_single_label(domain) == 0)
|
||||
|
||||
if ((!dns_name_is_single_label(domain) ||
|
||||
(!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) &&
|
||||
dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0)
|
||||
return DNS_SCOPE_MAYBE;
|
||||
|
||||
return DNS_SCOPE_NO;
|
||||
@ -371,7 +386,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) ||
|
||||
(s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) ||
|
||||
(dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */
|
||||
(dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
|
||||
!is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */
|
||||
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
|
||||
return DNS_SCOPE_MAYBE;
|
||||
@ -850,3 +865,45 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
|
||||
dns_cache_dump(&s->cache, f);
|
||||
}
|
||||
}
|
||||
|
||||
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
|
||||
assert(s);
|
||||
|
||||
/* Returns the list of *local* search domains -- not the
|
||||
* global ones. */
|
||||
|
||||
if (s->protocol != DNS_PROTOCOL_DNS)
|
||||
return NULL;
|
||||
|
||||
if (s->link)
|
||||
return s->link->search_domains;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dns_scope_has_search_domains(DnsScope *s) {
|
||||
assert(s);
|
||||
|
||||
/* Tests if there are *any* search domains suitable for this
|
||||
* scope. This means either local or global ones */
|
||||
|
||||
if (s->protocol != DNS_PROTOCOL_DNS)
|
||||
return false;
|
||||
|
||||
if (s->manager->search_domains)
|
||||
return true;
|
||||
|
||||
if (s->link && s->link->search_domains)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
|
||||
assert(s);
|
||||
|
||||
if (s->protocol != DNS_PROTOCOL_DNS)
|
||||
return false;
|
||||
|
||||
return dns_name_is_single_label(name);
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ struct DnsScope {
|
||||
|
||||
Link *link;
|
||||
|
||||
char **domains;
|
||||
|
||||
DnsCache cache;
|
||||
DnsZone zone;
|
||||
|
||||
@ -61,6 +59,7 @@ struct DnsScope {
|
||||
usec_t max_rtt;
|
||||
|
||||
Hashmap *transactions;
|
||||
LIST_HEAD(DnsQueryCandidate, query_candidates);
|
||||
|
||||
LIST_FIELDS(DnsScope, scopes);
|
||||
};
|
||||
@ -91,3 +90,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
|
||||
void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
|
||||
|
||||
void dns_scope_dump(DnsScope *s, FILE *f);
|
||||
|
||||
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
|
||||
bool dns_scope_has_search_domains(DnsScope *s);
|
||||
|
||||
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
|
||||
|
232
src/resolve/resolved-dns-search-domain.c
Normal file
232
src/resolve/resolved-dns-search-domain.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2015 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/>.
|
||||
***/
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
|
||||
int dns_search_domain_new(
|
||||
Manager *m,
|
||||
DnsSearchDomain **ret,
|
||||
DnsSearchDomainType type,
|
||||
Link *l,
|
||||
const char *name) {
|
||||
|
||||
_cleanup_free_ char *normalized = NULL;
|
||||
DnsSearchDomain *d;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
|
||||
assert(name);
|
||||
|
||||
r = dns_name_normalize(name, &normalized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (dns_name_is_root(normalized))
|
||||
return -EINVAL;
|
||||
|
||||
if (l) {
|
||||
if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
|
||||
return -E2BIG;
|
||||
} else {
|
||||
if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX)
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
d = new0(DnsSearchDomain, 1);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
d->n_ref = 1;
|
||||
d->manager = m;
|
||||
d->type = type;
|
||||
d->name = normalized;
|
||||
normalized = NULL;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case DNS_SEARCH_DOMAIN_LINK:
|
||||
d->link = l;
|
||||
LIST_APPEND(domains, l->search_domains, d);
|
||||
l->n_search_domains++;
|
||||
break;
|
||||
|
||||
case DNS_SERVER_SYSTEM:
|
||||
LIST_APPEND(domains, m->search_domains, d);
|
||||
m->n_search_domains++;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown search domain type");
|
||||
}
|
||||
|
||||
d->linked = true;
|
||||
|
||||
if (ret)
|
||||
*ret = d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
assert(d->n_ref > 0);
|
||||
d->n_ref++;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
assert(d->n_ref > 0);
|
||||
d->n_ref--;
|
||||
|
||||
if (d->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
free(d->name);
|
||||
free(d);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dns_search_domain_unlink(DnsSearchDomain *d) {
|
||||
assert(d);
|
||||
assert(d->manager);
|
||||
|
||||
if (!d->linked)
|
||||
return;
|
||||
|
||||
switch (d->type) {
|
||||
|
||||
case DNS_SEARCH_DOMAIN_LINK:
|
||||
assert(d->link);
|
||||
assert(d->link->n_search_domains > 0);
|
||||
LIST_REMOVE(domains, d->link->search_domains, d);
|
||||
d->link->n_search_domains--;
|
||||
break;
|
||||
|
||||
case DNS_SEARCH_DOMAIN_SYSTEM:
|
||||
assert(d->manager->n_search_domains > 0);
|
||||
LIST_REMOVE(domains, d->manager->search_domains, d);
|
||||
d->manager->n_search_domains--;
|
||||
break;
|
||||
}
|
||||
|
||||
d->linked = false;
|
||||
|
||||
dns_search_domain_unref(d);
|
||||
}
|
||||
|
||||
void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
|
||||
DnsSearchDomain *tail;
|
||||
|
||||
assert(d);
|
||||
|
||||
if (!d->marked)
|
||||
return;
|
||||
|
||||
d->marked = false;
|
||||
|
||||
if (!d->linked || !d->domains_next)
|
||||
return;
|
||||
|
||||
switch (d->type) {
|
||||
|
||||
case DNS_SEARCH_DOMAIN_LINK:
|
||||
assert(d->link);
|
||||
LIST_FIND_TAIL(domains, d, tail);
|
||||
LIST_REMOVE(domains, d->link->search_domains, d);
|
||||
LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
|
||||
break;
|
||||
|
||||
case DNS_SEARCH_DOMAIN_SYSTEM:
|
||||
LIST_FIND_TAIL(domains, d, tail);
|
||||
LIST_REMOVE(domains, d->manager->search_domains, d);
|
||||
LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown search domain type");
|
||||
}
|
||||
}
|
||||
|
||||
void dns_search_domain_unlink_all(DnsSearchDomain *first) {
|
||||
DnsSearchDomain *next;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
next = first->domains_next;
|
||||
dns_search_domain_unlink(first);
|
||||
|
||||
dns_search_domain_unlink_all(next);
|
||||
}
|
||||
|
||||
void dns_search_domain_unlink_marked(DnsSearchDomain *first) {
|
||||
DnsSearchDomain *next;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
next = first->domains_next;
|
||||
|
||||
if (first->marked)
|
||||
dns_search_domain_unlink(first);
|
||||
|
||||
dns_search_domain_unlink_marked(next);
|
||||
}
|
||||
|
||||
void dns_search_domain_mark_all(DnsSearchDomain *first) {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
first->marked = true;
|
||||
dns_search_domain_mark_all(first->domains_next);
|
||||
}
|
||||
|
||||
int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
|
||||
DnsSearchDomain *d;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
LIST_FOREACH(domains, d, first) {
|
||||
|
||||
r = dns_name_equal(name, d->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
*ret = d;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
75
src/resolve/resolved-dns-search-domain.h
Normal file
75
src/resolve/resolved-dns-search-domain.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2015 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/>.
|
||||
***/
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct DnsSearchDomain DnsSearchDomain;
|
||||
|
||||
typedef enum DnsSearchDomainType {
|
||||
DNS_SEARCH_DOMAIN_SYSTEM,
|
||||
DNS_SEARCH_DOMAIN_LINK,
|
||||
} DnsSearchDomainType;
|
||||
|
||||
#include "resolved-link.h"
|
||||
#include "resolved-manager.h"
|
||||
|
||||
struct DnsSearchDomain {
|
||||
Manager *manager;
|
||||
|
||||
unsigned n_ref;
|
||||
|
||||
DnsSearchDomainType type;
|
||||
Link *link;
|
||||
|
||||
char *name;
|
||||
|
||||
bool marked:1;
|
||||
|
||||
bool linked:1;
|
||||
LIST_FIELDS(DnsSearchDomain, domains);
|
||||
};
|
||||
|
||||
int dns_search_domain_new(
|
||||
Manager *m,
|
||||
DnsSearchDomain **ret,
|
||||
DnsSearchDomainType type,
|
||||
Link *link,
|
||||
const char *name);
|
||||
|
||||
DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
|
||||
DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d);
|
||||
|
||||
void dns_search_domain_unlink(DnsSearchDomain *d);
|
||||
void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d);
|
||||
|
||||
void dns_search_domain_unlink_all(DnsSearchDomain *first);
|
||||
void dns_search_domain_unlink_marked(DnsSearchDomain *first);
|
||||
void dns_search_domain_mark_all(DnsSearchDomain *first);
|
||||
|
||||
int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret);
|
||||
|
||||
static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
|
||||
return d ? d->name : NULL;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
|
@ -21,7 +21,9 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "siphash24.h"
|
||||
#include "string-util.h"
|
||||
|
||||
/* After how much time to repeat classic DNS requests */
|
||||
#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC)
|
||||
@ -35,36 +37,57 @@ int dns_server_new(
|
||||
int family,
|
||||
const union in_addr_union *in_addr) {
|
||||
|
||||
DnsServer *s, *tail;
|
||||
DnsServer *s;
|
||||
|
||||
assert(m);
|
||||
assert((type == DNS_SERVER_LINK) == !!l);
|
||||
assert(in_addr);
|
||||
|
||||
if (!IN_SET(family, AF_INET, AF_INET6))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (l) {
|
||||
if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX)
|
||||
return -E2BIG;
|
||||
} else {
|
||||
if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX)
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
s = new0(DnsServer, 1);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
s->n_ref = 1;
|
||||
s->manager = m;
|
||||
s->type = type;
|
||||
s->family = family;
|
||||
s->address = *in_addr;
|
||||
s->resend_timeout = DNS_TIMEOUT_MIN_USEC;
|
||||
|
||||
if (type == DNS_SERVER_LINK) {
|
||||
LIST_FIND_TAIL(servers, l->dns_servers, tail);
|
||||
LIST_INSERT_AFTER(servers, l->dns_servers, tail, s);
|
||||
s->link = l;
|
||||
} else if (type == DNS_SERVER_SYSTEM) {
|
||||
LIST_FIND_TAIL(servers, m->dns_servers, tail);
|
||||
LIST_INSERT_AFTER(servers, m->dns_servers, tail, s);
|
||||
} else if (type == DNS_SERVER_FALLBACK) {
|
||||
LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail);
|
||||
LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s);
|
||||
} else
|
||||
assert_not_reached("Unknown server type");
|
||||
switch (type) {
|
||||
|
||||
s->manager = m;
|
||||
case DNS_SERVER_LINK:
|
||||
s->link = l;
|
||||
LIST_APPEND(servers, l->dns_servers, s);
|
||||
l->n_dns_servers++;
|
||||
break;
|
||||
|
||||
case DNS_SERVER_SYSTEM:
|
||||
LIST_APPEND(servers, m->dns_servers, s);
|
||||
m->n_dns_servers++;
|
||||
break;
|
||||
|
||||
case DNS_SERVER_FALLBACK:
|
||||
LIST_APPEND(servers, m->fallback_dns_servers, s);
|
||||
m->n_dns_servers++;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown server type");
|
||||
}
|
||||
|
||||
s->linked = true;
|
||||
|
||||
/* A new DNS server that isn't fallback is added and the one
|
||||
* we used so far was a fallback one? Then let's try to pick
|
||||
@ -85,56 +108,127 @@ DnsServer* dns_server_ref(DnsServer *s) {
|
||||
return NULL;
|
||||
|
||||
assert(s->n_ref > 0);
|
||||
|
||||
s->n_ref ++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static DnsServer* dns_server_free(DnsServer *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
if (s->link && s->link->current_dns_server == s)
|
||||
link_set_dns_server(s->link, NULL);
|
||||
|
||||
if (s->manager && s->manager->current_dns_server == s)
|
||||
manager_set_dns_server(s->manager, NULL);
|
||||
|
||||
free(s);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsServer* dns_server_unref(DnsServer *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
assert(s->n_ref > 0);
|
||||
s->n_ref --;
|
||||
|
||||
if (s->n_ref == 1)
|
||||
dns_server_free(s);
|
||||
else
|
||||
s->n_ref --;
|
||||
if (s->n_ref > 0)
|
||||
return NULL;
|
||||
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dns_server_unlink(DnsServer *s) {
|
||||
assert(s);
|
||||
assert(s->manager);
|
||||
|
||||
/* This removes the specified server from the linked list of
|
||||
* servers, but any server might still stay around if it has
|
||||
* refs, for example from an ongoing transaction. */
|
||||
|
||||
if (!s->linked)
|
||||
return;
|
||||
|
||||
switch (s->type) {
|
||||
|
||||
case DNS_SERVER_LINK:
|
||||
assert(s->link);
|
||||
assert(s->link->n_dns_servers > 0);
|
||||
LIST_REMOVE(servers, s->link->dns_servers, s);
|
||||
break;
|
||||
|
||||
case DNS_SERVER_SYSTEM:
|
||||
assert(s->manager->n_dns_servers > 0);
|
||||
LIST_REMOVE(servers, s->manager->dns_servers, s);
|
||||
s->manager->n_dns_servers--;
|
||||
break;
|
||||
|
||||
case DNS_SERVER_FALLBACK:
|
||||
assert(s->manager->n_dns_servers > 0);
|
||||
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
|
||||
s->manager->n_dns_servers--;
|
||||
break;
|
||||
}
|
||||
|
||||
s->linked = false;
|
||||
|
||||
if (s->link && s->link->current_dns_server == s)
|
||||
link_set_dns_server(s->link, NULL);
|
||||
|
||||
if (s->manager->current_dns_server == s)
|
||||
manager_set_dns_server(s->manager, NULL);
|
||||
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
void dns_server_move_back_and_unmark(DnsServer *s) {
|
||||
DnsServer *tail;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!s->marked)
|
||||
return;
|
||||
|
||||
s->marked = false;
|
||||
|
||||
if (!s->linked || !s->servers_next)
|
||||
return;
|
||||
|
||||
/* Move us to the end of the list, so that the order is
|
||||
* strictly kept, if we are not at the end anyway. */
|
||||
|
||||
switch (s->type) {
|
||||
|
||||
case DNS_SERVER_LINK:
|
||||
assert(s->link);
|
||||
LIST_FIND_TAIL(servers, s, tail);
|
||||
LIST_REMOVE(servers, s->link->dns_servers, s);
|
||||
LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s);
|
||||
break;
|
||||
|
||||
case DNS_SERVER_SYSTEM:
|
||||
LIST_FIND_TAIL(servers, s, tail);
|
||||
LIST_REMOVE(servers, s->manager->dns_servers, s);
|
||||
LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s);
|
||||
break;
|
||||
|
||||
case DNS_SERVER_FALLBACK:
|
||||
LIST_FIND_TAIL(servers, s, tail);
|
||||
LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
|
||||
LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown server type");
|
||||
}
|
||||
}
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, usec_t rtt) {
|
||||
assert(s);
|
||||
|
||||
if (rtt > s->max_rtt) {
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2),
|
||||
DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
if (rtt <= s->max_rtt)
|
||||
return;
|
||||
|
||||
s->max_rtt = rtt;
|
||||
s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
void dns_server_packet_lost(DnsServer *s, usec_t usec) {
|
||||
assert(s);
|
||||
|
||||
if (s->resend_timeout <= usec)
|
||||
s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
|
||||
if (s->resend_timeout > usec)
|
||||
return;
|
||||
|
||||
s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
static void dns_server_hash_func(const void *p, struct siphash *state) {
|
||||
@ -161,3 +255,140 @@ const struct hash_ops dns_server_hash_ops = {
|
||||
.hash = dns_server_hash_func,
|
||||
.compare = dns_server_compare_func
|
||||
};
|
||||
|
||||
void dns_server_unlink_all(DnsServer *first) {
|
||||
DnsServer *next;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
next = first->servers_next;
|
||||
dns_server_unlink(first);
|
||||
|
||||
dns_server_unlink_all(next);
|
||||
}
|
||||
|
||||
void dns_server_unlink_marked(DnsServer *first) {
|
||||
DnsServer *next;
|
||||
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
next = first->servers_next;
|
||||
|
||||
if (first->marked)
|
||||
dns_server_unlink(first);
|
||||
|
||||
dns_server_unlink_marked(next);
|
||||
}
|
||||
|
||||
void dns_server_mark_all(DnsServer *first) {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
first->marked = true;
|
||||
dns_server_mark_all(first->servers_next);
|
||||
}
|
||||
|
||||
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) {
|
||||
DnsServer *s;
|
||||
|
||||
LIST_FOREACH(servers, s, first)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
|
||||
return s;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
|
||||
assert(m);
|
||||
|
||||
switch (t) {
|
||||
|
||||
case DNS_SERVER_SYSTEM:
|
||||
return m->dns_servers;
|
||||
|
||||
case DNS_SERVER_FALLBACK:
|
||||
return m->fallback_dns_servers;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
||||
assert(m);
|
||||
|
||||
if (m->current_dns_server == s)
|
||||
return s;
|
||||
|
||||
if (s) {
|
||||
_cleanup_free_ char *ip = NULL;
|
||||
|
||||
in_addr_to_string(s->family, &s->address, &ip);
|
||||
log_info("Switching to system DNS server %s.", strna(ip));
|
||||
}
|
||||
|
||||
dns_server_unref(m->current_dns_server);
|
||||
m->current_dns_server = dns_server_ref(s);
|
||||
|
||||
if (m->unicast_scope)
|
||||
dns_cache_flush(&m->unicast_scope->cache);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
DnsServer *manager_get_dns_server(Manager *m) {
|
||||
Link *l;
|
||||
assert(m);
|
||||
|
||||
/* Try to read updates resolv.conf */
|
||||
manager_read_resolv_conf(m);
|
||||
|
||||
/* If no DNS server was chose so far, pick the first one */
|
||||
if (!m->current_dns_server)
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
|
||||
if (!m->current_dns_server) {
|
||||
bool found = false;
|
||||
Iterator i;
|
||||
|
||||
/* No DNS servers configured, let's see if there are
|
||||
* any on any links. If not, we use the fallback
|
||||
* servers */
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i)
|
||||
if (l->dns_servers) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
manager_set_dns_server(m, m->fallback_dns_servers);
|
||||
}
|
||||
|
||||
return m->current_dns_server;
|
||||
}
|
||||
|
||||
void manager_next_dns_server(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
/* If there's currently no DNS server set, then the next
|
||||
* manager_get_dns_server() will find one */
|
||||
if (!m->current_dns_server)
|
||||
return;
|
||||
|
||||
/* Change to the next one, but make sure to follow the linked
|
||||
* list only if the server is still linked. */
|
||||
if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
|
||||
manager_set_dns_server(m, m->current_dns_server->servers_next);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there was no next one, then start from the beginning of
|
||||
* the list */
|
||||
if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
|
||||
manager_set_dns_server(m, m->fallback_dns_servers);
|
||||
else
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "in-addr-util.h"
|
||||
|
||||
typedef struct DnsServer DnsServer;
|
||||
typedef enum DnsServerSource DnsServerSource;
|
||||
|
||||
typedef enum DnsServerType {
|
||||
DNS_SERVER_SYSTEM,
|
||||
@ -32,6 +31,7 @@ typedef enum DnsServerType {
|
||||
DNS_SERVER_LINK,
|
||||
} DnsServerType;
|
||||
|
||||
#include "resolved-manager.h"
|
||||
#include "resolved-link.h"
|
||||
|
||||
struct DnsServer {
|
||||
@ -40,7 +40,6 @@ struct DnsServer {
|
||||
unsigned n_ref;
|
||||
|
||||
DnsServerType type;
|
||||
|
||||
Link *link;
|
||||
|
||||
int family;
|
||||
@ -51,23 +50,40 @@ struct DnsServer {
|
||||
|
||||
bool marked:1;
|
||||
|
||||
/* If linked is set, then this server appears in the servers linked list */
|
||||
bool linked:1;
|
||||
LIST_FIELDS(DnsServer, servers);
|
||||
};
|
||||
|
||||
int dns_server_new(
|
||||
Manager *m,
|
||||
DnsServer **s,
|
||||
DnsServer **ret,
|
||||
DnsServerType type,
|
||||
Link *l,
|
||||
Link *link,
|
||||
int family,
|
||||
const union in_addr_union *address);
|
||||
|
||||
DnsServer* dns_server_ref(DnsServer *s);
|
||||
DnsServer* dns_server_unref(DnsServer *s);
|
||||
|
||||
void dns_server_unlink(DnsServer *s);
|
||||
void dns_server_move_back_and_unmark(DnsServer *s);
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, usec_t rtt);
|
||||
void dns_server_packet_lost(DnsServer *s, usec_t usec);
|
||||
|
||||
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
|
||||
|
||||
void dns_server_unlink_all(DnsServer *first);
|
||||
void dns_server_unlink_marked(DnsServer *first);
|
||||
void dns_server_mark_all(DnsServer *first);
|
||||
|
||||
DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
|
||||
|
||||
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
|
||||
DnsServer *manager_get_dns_server(Manager *m);
|
||||
void manager_next_dns_server(Manager *m);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
|
||||
|
||||
extern const struct hash_ops dns_server_hash_ops;
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "string-table.h"
|
||||
|
||||
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
||||
DnsQuery *q;
|
||||
DnsQueryCandidate *c;
|
||||
DnsZoneItem *i;
|
||||
|
||||
if (!t)
|
||||
@ -56,9 +56,10 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
||||
|
||||
dns_resource_key_unref(t->key);
|
||||
|
||||
while ((q = set_steal_first(t->queries)))
|
||||
set_remove(q->transactions, t);
|
||||
set_free(t->queries);
|
||||
while ((c = set_steal_first(t->query_candidates)))
|
||||
set_remove(c->transactions, t);
|
||||
|
||||
set_free(t->query_candidates);
|
||||
|
||||
while ((i = set_steal_first(t->zone_items)))
|
||||
i->probe_transaction = NULL;
|
||||
@ -76,7 +77,7 @@ void dns_transaction_gc(DnsTransaction *t) {
|
||||
if (t->block_gc > 0)
|
||||
return;
|
||||
|
||||
if (set_isempty(t->queries) && set_isempty(t->zone_items))
|
||||
if (set_isempty(t->query_candidates) && set_isempty(t->zone_items))
|
||||
dns_transaction_free(t);
|
||||
}
|
||||
|
||||
@ -181,7 +182,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
|
||||
}
|
||||
|
||||
void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
||||
DnsQuery *q;
|
||||
DnsQueryCandidate *c;
|
||||
DnsZoneItem *z;
|
||||
Iterator i;
|
||||
|
||||
@ -205,8 +206,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
||||
/* Notify all queries that are interested, but make sure the
|
||||
* transaction isn't freed while we are still looking at it */
|
||||
t->block_gc++;
|
||||
SET_FOREACH(q, t->queries, i)
|
||||
dns_query_ready(q);
|
||||
SET_FOREACH(c, t->query_candidates, i)
|
||||
dns_query_candidate_ready(c);
|
||||
SET_FOREACH(z, t->zone_items, i)
|
||||
dns_zone_item_ready(z);
|
||||
t->block_gc--;
|
||||
|
@ -71,9 +71,10 @@ struct DnsTransaction {
|
||||
/* TCP connection logic, if we need it */
|
||||
DnsStream *stream;
|
||||
|
||||
/* Queries this transaction is referenced by and that shall be
|
||||
* notified about this specific transaction completing. */
|
||||
Set *queries;
|
||||
/* Query candidates this transaction is referenced by and that
|
||||
* shall be notified about this specific transaction
|
||||
* completing. */
|
||||
Set *query_candidates;
|
||||
|
||||
/* Zone items this transaction is referenced by and that shall
|
||||
* be notified about completion. */
|
||||
|
@ -311,7 +311,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
|
||||
|
||||
found = true;
|
||||
|
||||
k = dns_resource_key_match_rr(key, j->rr);
|
||||
k = dns_resource_key_match_rr(key, j->rr, NULL);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k > 0) {
|
||||
@ -381,7 +381,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
|
||||
if (j->state != DNS_ZONE_ITEM_PROBING)
|
||||
tentative = false;
|
||||
|
||||
k = dns_resource_key_match_rr(key, j->rr);
|
||||
k = dns_resource_key_match_rr(key, j->rr, NULL);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k > 0) {
|
||||
|
@ -14,6 +14,7 @@ struct ConfigPerfItem;
|
||||
%struct-type
|
||||
%includes
|
||||
%%
|
||||
Resolve.DNS, config_parse_dnsv, DNS_SERVER_SYSTEM, 0
|
||||
Resolve.FallbackDNS, config_parse_dnsv, DNS_SERVER_FALLBACK, 0
|
||||
Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support)
|
||||
Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
|
||||
Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
|
||||
Resolve.Domains, config_parse_search_domains, 0, 0
|
||||
Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support)
|
||||
|
@ -65,19 +65,15 @@ Link *link_free(Link *l) {
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
dns_server_unlink_marked(l->dns_servers);
|
||||
dns_search_domain_unlink_all(l->search_domains);
|
||||
|
||||
while (l->addresses)
|
||||
link_address_free(l->addresses);
|
||||
|
||||
if (l->manager)
|
||||
hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
|
||||
|
||||
while (l->dns_servers) {
|
||||
DnsServer *s = l->dns_servers;
|
||||
|
||||
LIST_REMOVE(servers, l->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
dns_scope_free(l->unicast_scope);
|
||||
dns_scope_free(l->llmnr_ipv4_scope);
|
||||
dns_scope_free(l->llmnr_ipv6_scope);
|
||||
@ -158,7 +154,6 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
|
||||
static int link_update_dns_servers(Link *l) {
|
||||
_cleanup_strv_free_ char **nameservers = NULL;
|
||||
char **nameserver;
|
||||
DnsServer *s, *nx;
|
||||
int r;
|
||||
|
||||
assert(l);
|
||||
@ -167,20 +162,20 @@ static int link_update_dns_servers(Link *l) {
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
LIST_FOREACH(servers, s, l->dns_servers)
|
||||
s->marked = true;
|
||||
dns_server_mark_all(l->dns_servers);
|
||||
|
||||
STRV_FOREACH(nameserver, nameservers) {
|
||||
union in_addr_union a;
|
||||
DnsServer *s;
|
||||
int family;
|
||||
|
||||
r = in_addr_from_string_auto(*nameserver, &family, &a);
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
s = link_find_dns_server(l, family, &a);
|
||||
s = dns_server_find(l->dns_servers, family, &a);
|
||||
if (s)
|
||||
s->marked = false;
|
||||
dns_server_move_back_and_unmark(s);
|
||||
else {
|
||||
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a);
|
||||
if (r < 0)
|
||||
@ -188,22 +183,11 @@ static int link_update_dns_servers(Link *l) {
|
||||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers)
|
||||
if (s->marked) {
|
||||
LIST_REMOVE(servers, l->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
dns_server_unlink_marked(l->dns_servers);
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
while (l->dns_servers) {
|
||||
s = l->dns_servers;
|
||||
|
||||
LIST_REMOVE(servers, l->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
dns_server_unlink_all(l->dns_servers);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -236,29 +220,56 @@ clear:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int link_update_domains(Link *l) {
|
||||
static int link_update_search_domains(Link *l) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
if (!l->unicast_scope)
|
||||
return 0;
|
||||
assert(l);
|
||||
|
||||
l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
|
||||
|
||||
r = sd_network_link_get_domains(l->ifindex,
|
||||
&l->unicast_scope->domains);
|
||||
r = sd_network_link_get_domains(l->ifindex, &domains);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto clear;
|
||||
|
||||
dns_search_domain_mark_all(l->search_domains);
|
||||
|
||||
STRV_FOREACH(i, domains) {
|
||||
DnsSearchDomain *d;
|
||||
|
||||
r = dns_search_domain_find(l->search_domains, *i, &d);
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
|
||||
if (r > 0)
|
||||
dns_search_domain_move_back_and_unmark(d);
|
||||
else {
|
||||
r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
}
|
||||
}
|
||||
|
||||
dns_search_domain_unlink_marked(l->search_domains);
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
dns_search_domain_unlink_all(l->search_domains);
|
||||
return r;
|
||||
}
|
||||
|
||||
int link_update_monitor(Link *l) {
|
||||
int r;
|
||||
|
||||
assert(l);
|
||||
|
||||
link_update_dns_servers(l);
|
||||
link_update_llmnr_support(l);
|
||||
link_allocate_scopes(l);
|
||||
link_update_domains(l);
|
||||
|
||||
r = link_update_search_domains(l);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
|
||||
|
||||
link_add_rrs(l, false);
|
||||
|
||||
return 0;
|
||||
@ -303,17 +314,6 @@ LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *i
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) {
|
||||
DnsServer *s;
|
||||
|
||||
assert(l);
|
||||
|
||||
LIST_FOREACH(servers, s, l->dns_servers)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, in_addr))
|
||||
return s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
|
||||
assert(l);
|
||||
|
||||
@ -327,7 +327,8 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
|
||||
log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
|
||||
}
|
||||
|
||||
l->current_dns_server = s;
|
||||
dns_server_unref(l->current_dns_server);
|
||||
l->current_dns_server = dns_server_ref(s);
|
||||
|
||||
if (l->unicast_scope)
|
||||
dns_cache_flush(&l->unicast_scope->cache);
|
||||
@ -350,7 +351,9 @@ void link_next_dns_server(Link *l) {
|
||||
if (!l->current_dns_server)
|
||||
return;
|
||||
|
||||
if (l->current_dns_server->servers_next) {
|
||||
/* Change to the next one, but make sure to follow the linked
|
||||
* list only if this server is actually still linked. */
|
||||
if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
|
||||
link_set_dns_server(l, l->current_dns_server->servers_next);
|
||||
return;
|
||||
}
|
||||
|
@ -30,8 +30,13 @@ typedef struct Link Link;
|
||||
typedef struct LinkAddress LinkAddress;
|
||||
|
||||
#include "resolved-dns-rr.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-manager.h"
|
||||
|
||||
#define LINK_SEARCH_DOMAINS_MAX 32
|
||||
#define LINK_DNS_SERVERS_MAX 32
|
||||
|
||||
struct LinkAddress {
|
||||
Link *link;
|
||||
|
||||
@ -56,6 +61,10 @@ struct Link {
|
||||
|
||||
LIST_HEAD(DnsServer, dns_servers);
|
||||
DnsServer *current_dns_server;
|
||||
unsigned n_dns_servers;
|
||||
|
||||
LIST_HEAD(DnsSearchDomain, search_domains);
|
||||
unsigned n_search_domains;
|
||||
|
||||
Support llmnr_support;
|
||||
|
||||
@ -76,7 +85,6 @@ LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *i
|
||||
void link_add_rrs(Link *l, bool force_remove);
|
||||
|
||||
DnsServer* link_set_dns_server(Link *l, DnsServer *s);
|
||||
DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr);
|
||||
DnsServer* link_get_dns_server(Link *l);
|
||||
void link_next_dns_server(Link *l);
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <resolv.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "af-list.h"
|
||||
@ -40,6 +39,7 @@
|
||||
#include "resolved-conf.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-manager.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
@ -351,7 +351,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = dns_label_escape(label, r, &n);
|
||||
r = dns_label_escape_new(label, r, &n);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to escape host name: %m");
|
||||
|
||||
@ -476,10 +476,7 @@ int manager_new(Manager **ret) {
|
||||
|
||||
m->llmnr_support = SUPPORT_YES;
|
||||
m->read_resolv_conf = true;
|
||||
|
||||
r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
m->need_builtin_fallbacks = true;
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
if (r < 0)
|
||||
@ -536,15 +533,16 @@ Manager *manager_free(Manager *m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
dns_server_unlink_all(m->dns_servers);
|
||||
dns_server_unlink_all(m->fallback_dns_servers);
|
||||
dns_search_domain_unlink_all(m->search_domains);
|
||||
|
||||
while ((l = hashmap_first(m->links)))
|
||||
link_free(l);
|
||||
|
||||
while (m->dns_queries)
|
||||
dns_query_free(m->dns_queries);
|
||||
|
||||
manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
|
||||
manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
|
||||
|
||||
dns_scope_free(m->unicast_scope);
|
||||
|
||||
hashmap_free(m->links);
|
||||
@ -579,294 +577,6 @@ Manager *manager_free(Manager *m) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int manager_read_resolv_conf(Manager *m) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct stat st, own;
|
||||
char line[LINE_MAX];
|
||||
DnsServer *s, *nx;
|
||||
usec_t t;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Reads the system /etc/resolv.conf, if it exists and is not
|
||||
* symlinked to our own resolv.conf instance */
|
||||
|
||||
if (!m->read_resolv_conf)
|
||||
return 0;
|
||||
|
||||
r = stat("/etc/resolv.conf", &st);
|
||||
if (r < 0) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
|
||||
r = -errno;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
/* Have we already seen the file? */
|
||||
t = timespec_load(&st.st_mtim);
|
||||
if (t == m->resolv_conf_mtime)
|
||||
return 0;
|
||||
|
||||
m->resolv_conf_mtime = t;
|
||||
|
||||
/* Is it symlinked to our own file? */
|
||||
if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
|
||||
st.st_dev == own.st_dev &&
|
||||
st.st_ino == own.st_ino) {
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
f = fopen("/etc/resolv.conf", "re");
|
||||
if (!f) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
|
||||
r = -errno;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
if (fstat(fileno(f), &st) < 0) {
|
||||
r = log_error_errno(errno, "Failed to stat open file: %m");
|
||||
goto clear;
|
||||
}
|
||||
|
||||
LIST_FOREACH(servers, s, m->dns_servers)
|
||||
s->marked = true;
|
||||
|
||||
FOREACH_LINE(line, f, r = -errno; goto clear) {
|
||||
union in_addr_union address;
|
||||
int family;
|
||||
char *l;
|
||||
const char *a;
|
||||
|
||||
truncate_nl(line);
|
||||
|
||||
l = strstrip(line);
|
||||
if (*l == '#' || *l == ';')
|
||||
continue;
|
||||
|
||||
a = first_word(l, "nameserver");
|
||||
if (!a)
|
||||
continue;
|
||||
|
||||
r = in_addr_from_string_auto(a, &family, &address);
|
||||
if (r < 0) {
|
||||
log_warning("Failed to parse name server %s.", a);
|
||||
continue;
|
||||
}
|
||||
|
||||
LIST_FOREACH(servers, s, m->dns_servers)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, &address) > 0)
|
||||
break;
|
||||
|
||||
if (s)
|
||||
s->marked = false;
|
||||
else {
|
||||
r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address);
|
||||
if (r < 0)
|
||||
goto clear;
|
||||
}
|
||||
}
|
||||
|
||||
LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers)
|
||||
if (s->marked) {
|
||||
LIST_REMOVE(servers, m->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
/* Whenever /etc/resolv.conf changes, start using the first
|
||||
* DNS server of it. This is useful to deal with broken
|
||||
* network managing implementations (like NetworkManager),
|
||||
* that when connecting to a VPN place both the VPN DNS
|
||||
* servers and the local ones in /etc/resolv.conf. Without
|
||||
* resetting the DNS server to use back to the first entry we
|
||||
* will continue to use the local one thus being unable to
|
||||
* resolve VPN domains. */
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
while (m->dns_servers) {
|
||||
s = m->dns_servers;
|
||||
|
||||
LIST_REMOVE(servers, m->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(count);
|
||||
|
||||
r = in_addr_to_string(s->family, &s->address, &t);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*count == MAXNS)
|
||||
fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
|
||||
|
||||
fprintf(f, "nameserver %s\n", t);
|
||||
(*count) ++;
|
||||
}
|
||||
|
||||
static void write_resolv_conf_search(
|
||||
const char *domain, FILE *f,
|
||||
unsigned *count,
|
||||
unsigned *length) {
|
||||
|
||||
assert(domain);
|
||||
assert(f);
|
||||
assert(length);
|
||||
|
||||
if (*count >= MAXDNSRCH ||
|
||||
*length + strlen(domain) > 256) {
|
||||
if (*count == MAXDNSRCH)
|
||||
fputs(" # Too many search domains configured, remaining ones ignored.", f);
|
||||
if (*length <= 256)
|
||||
fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, " %s", domain);
|
||||
|
||||
(*length) += strlen(domain);
|
||||
(*count) ++;
|
||||
}
|
||||
|
||||
static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
|
||||
Iterator i;
|
||||
|
||||
fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
|
||||
"# Third party programs must not access this file directly, but\n"
|
||||
"# only through the symlink at /etc/resolv.conf. To manage\n"
|
||||
"# resolv.conf(5) in a different way, replace the symlink by a\n"
|
||||
"# static file or a different symlink.\n\n", f);
|
||||
|
||||
if (ordered_set_isempty(dns))
|
||||
fputs("# No DNS servers known.\n", f);
|
||||
else {
|
||||
DnsServer *s;
|
||||
unsigned count = 0;
|
||||
|
||||
ORDERED_SET_FOREACH(s, dns, i)
|
||||
write_resolv_conf_server(s, f, &count);
|
||||
}
|
||||
|
||||
if (!ordered_set_isempty(domains)) {
|
||||
unsigned length = 0, count = 0;
|
||||
char *domain;
|
||||
|
||||
fputs("search", f);
|
||||
ORDERED_SET_FOREACH(domain, domains, i)
|
||||
write_resolv_conf_search(domain, f, &count, &length);
|
||||
fputs("\n", f);
|
||||
}
|
||||
|
||||
return fflush_and_check(f);
|
||||
}
|
||||
|
||||
int manager_write_resolv_conf(Manager *m) {
|
||||
static const char path[] = "/run/systemd/resolve/resolv.conf";
|
||||
_cleanup_free_ char *temp_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
|
||||
DnsServer *s;
|
||||
Iterator i;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Read the system /etc/resolv.conf first */
|
||||
manager_read_resolv_conf(m);
|
||||
|
||||
/* Add the full list to a set, to filter out duplicates */
|
||||
dns = ordered_set_new(&dns_server_hash_ops);
|
||||
if (!dns)
|
||||
return -ENOMEM;
|
||||
|
||||
domains = ordered_set_new(&dns_name_hash_ops);
|
||||
if (!domains)
|
||||
return -ENOMEM;
|
||||
|
||||
/* First add the system-wide servers */
|
||||
LIST_FOREACH(servers, s, m->dns_servers) {
|
||||
r = ordered_set_put(dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Then, add the per-link servers and domains */
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
char **domain;
|
||||
|
||||
LIST_FOREACH(servers, s, l->dns_servers) {
|
||||
r = ordered_set_put(dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!l->unicast_scope)
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(domain, l->unicast_scope->domains) {
|
||||
r = ordered_set_put(domains, *domain);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we found nothing, add the fallback servers */
|
||||
if (ordered_set_isempty(dns)) {
|
||||
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
|
||||
r = ordered_set_put(dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = fopen_temporary_label(path, path, &f, &temp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fchmod(fileno(f), 0644);
|
||||
|
||||
r = write_resolv_conf_contents(f, dns, domains);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (rename(temp_path, path) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
(void) unlink(path);
|
||||
(void) unlink(temp_path);
|
||||
return r;
|
||||
}
|
||||
|
||||
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
union {
|
||||
@ -1174,97 +884,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
|
||||
DnsServer *s;
|
||||
|
||||
assert(m);
|
||||
assert(in_addr);
|
||||
|
||||
LIST_FOREACH(servers, s, m->dns_servers)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
|
||||
return s;
|
||||
|
||||
LIST_FOREACH(servers, s, m->fallback_dns_servers)
|
||||
if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
|
||||
return s;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
||||
assert(m);
|
||||
|
||||
if (m->current_dns_server == s)
|
||||
return s;
|
||||
|
||||
if (s) {
|
||||
_cleanup_free_ char *ip = NULL;
|
||||
|
||||
in_addr_to_string(s->family, &s->address, &ip);
|
||||
log_info("Switching to system DNS server %s.", strna(ip));
|
||||
}
|
||||
|
||||
m->current_dns_server = s;
|
||||
|
||||
if (m->unicast_scope)
|
||||
dns_cache_flush(&m->unicast_scope->cache);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
DnsServer *manager_get_dns_server(Manager *m) {
|
||||
Link *l;
|
||||
assert(m);
|
||||
|
||||
/* Try to read updates resolv.conf */
|
||||
manager_read_resolv_conf(m);
|
||||
|
||||
if (!m->current_dns_server)
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
|
||||
if (!m->current_dns_server) {
|
||||
bool found = false;
|
||||
Iterator i;
|
||||
|
||||
/* No DNS servers configured, let's see if there are
|
||||
* any on any links. If not, we use the fallback
|
||||
* servers */
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i)
|
||||
if (l->dns_servers) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
manager_set_dns_server(m, m->fallback_dns_servers);
|
||||
}
|
||||
|
||||
return m->current_dns_server;
|
||||
}
|
||||
|
||||
void manager_next_dns_server(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
/* If there's currently no DNS server set, then the next
|
||||
* manager_get_dns_server() will find one */
|
||||
if (!m->current_dns_server)
|
||||
return;
|
||||
|
||||
/* Change to the next one */
|
||||
if (m->current_dns_server->servers_next) {
|
||||
manager_set_dns_server(m, m->current_dns_server->servers_next);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there was no next one, then start from the beginning of
|
||||
* the list */
|
||||
if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
|
||||
manager_set_dns_server(m, m->fallback_dns_servers);
|
||||
else
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
}
|
||||
|
||||
uint32_t manager_find_mtu(Manager *m) {
|
||||
uint32_t mtu = 0;
|
||||
Link *l;
|
||||
@ -1418,28 +1037,6 @@ void manager_verify_all(Manager *m) {
|
||||
dns_zone_verify_all(&s->zone);
|
||||
}
|
||||
|
||||
void manager_flush_dns_servers(Manager *m, DnsServerType t) {
|
||||
DnsServer *s;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (t == DNS_SERVER_SYSTEM)
|
||||
while (m->dns_servers) {
|
||||
s = m->dns_servers;
|
||||
|
||||
LIST_REMOVE(servers, m->dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
|
||||
if (t == DNS_SERVER_FALLBACK)
|
||||
while (m->fallback_dns_servers) {
|
||||
s = m->fallback_dns_servers;
|
||||
|
||||
LIST_REMOVE(servers, m->fallback_dns_servers, s);
|
||||
dns_server_unref(s);
|
||||
}
|
||||
}
|
||||
|
||||
int manager_is_own_hostname(Manager *m, const char *name) {
|
||||
int r;
|
||||
|
||||
@ -1458,6 +1055,88 @@ int manager_is_own_hostname(Manager *m, const char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
|
||||
DnsServer *s;
|
||||
Iterator i;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(dns);
|
||||
|
||||
r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* First add the system-wide servers and domains */
|
||||
LIST_FOREACH(servers, s, m->dns_servers) {
|
||||
r = ordered_set_put(*dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Then, add the per-link servers */
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
LIST_FOREACH(servers, s, l->dns_servers) {
|
||||
r = ordered_set_put(*dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we found nothing, add the fallback servers */
|
||||
if (ordered_set_isempty(*dns)) {
|
||||
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
|
||||
r = ordered_set_put(*dns, s);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
|
||||
DnsSearchDomain *d;
|
||||
Iterator i;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(domains);
|
||||
|
||||
r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(domains, d, m->search_domains) {
|
||||
r = ordered_set_put(*domains, d->name);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(l, m->links, i) {
|
||||
|
||||
LIST_FOREACH(domains, d, l->search_domains) {
|
||||
r = ordered_set_put(*domains, d->name);
|
||||
if (r == -EEXIST)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const support_table[_SUPPORT_MAX] = {
|
||||
[SUPPORT_NO] = "no",
|
||||
[SUPPORT_YES] = "yes",
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
#include "ordered-set.h"
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef enum Support Support;
|
||||
@ -40,9 +41,14 @@ enum Support {
|
||||
};
|
||||
|
||||
#include "resolved-dns-query.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-dns-stream.h"
|
||||
#include "resolved-link.h"
|
||||
|
||||
#define MANAGER_SEARCH_DOMAINS_MAX 32
|
||||
#define MANAGER_DNS_SERVERS_MAX 32
|
||||
|
||||
struct Manager {
|
||||
sd_event *event;
|
||||
|
||||
@ -68,9 +74,15 @@ struct Manager {
|
||||
/* Unicast dns */
|
||||
LIST_HEAD(DnsServer, dns_servers);
|
||||
LIST_HEAD(DnsServer, fallback_dns_servers);
|
||||
unsigned n_dns_servers; /* counts both main and fallback */
|
||||
DnsServer *current_dns_server;
|
||||
|
||||
bool read_resolv_conf;
|
||||
LIST_HEAD(DnsSearchDomain, search_domains);
|
||||
unsigned n_search_domains;
|
||||
|
||||
bool need_builtin_fallbacks:1;
|
||||
|
||||
bool read_resolv_conf:1;
|
||||
usec_t resolv_conf_mtime;
|
||||
|
||||
LIST_HEAD(DnsScope, dns_scopes);
|
||||
@ -113,13 +125,6 @@ int manager_new(Manager **ret);
|
||||
Manager* manager_free(Manager *m);
|
||||
|
||||
int manager_start(Manager *m);
|
||||
int manager_read_resolv_conf(Manager *m);
|
||||
int manager_write_resolv_conf(Manager *m);
|
||||
|
||||
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
|
||||
DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr);
|
||||
DnsServer *manager_get_dns_server(Manager *m);
|
||||
void manager_next_dns_server(Manager *m);
|
||||
|
||||
uint32_t manager_find_mtu(Manager *m);
|
||||
|
||||
@ -138,13 +143,14 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
|
||||
|
||||
void manager_verify_all(Manager *m);
|
||||
|
||||
void manager_flush_dns_servers(Manager *m, DnsServerType t);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
#define EXTRA_CMSG_SPACE 1024
|
||||
|
||||
int manager_is_own_hostname(Manager *m, const char *name);
|
||||
|
||||
int manager_compile_dns_servers(Manager *m, OrderedSet **servers);
|
||||
int manager_compile_search_domains(Manager *m, OrderedSet **domains);
|
||||
|
||||
const char* support_to_string(Support p) _const_;
|
||||
int support_from_string(const char *s) _pure_;
|
||||
|
266
src/resolve/resolved-resolv-conf.c
Normal file
266
src/resolve/resolved-resolv-conf.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2014 Tom Gundersen <teg@jklm.no>
|
||||
|
||||
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/>.
|
||||
***/
|
||||
|
||||
#include <resolv.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio-label.h"
|
||||
#include "fileio.h"
|
||||
#include "ordered-set.h"
|
||||
#include "resolved-conf.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
int manager_read_resolv_conf(Manager *m) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct stat st, own;
|
||||
char line[LINE_MAX];
|
||||
usec_t t;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Reads the system /etc/resolv.conf, if it exists and is not
|
||||
* symlinked to our own resolv.conf instance */
|
||||
|
||||
if (!m->read_resolv_conf)
|
||||
return 0;
|
||||
|
||||
r = stat("/etc/resolv.conf", &st);
|
||||
if (r < 0) {
|
||||
if (errno == ENOENT)
|
||||
r = 0;
|
||||
else
|
||||
r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
|
||||
goto clear;
|
||||
}
|
||||
|
||||
/* Have we already seen the file? */
|
||||
t = timespec_load(&st.st_mtim);
|
||||
if (t == m->resolv_conf_mtime)
|
||||
return 0;
|
||||
|
||||
m->resolv_conf_mtime = t;
|
||||
|
||||
/* Is it symlinked to our own file? */
|
||||
if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
|
||||
st.st_dev == own.st_dev &&
|
||||
st.st_ino == own.st_ino) {
|
||||
r = 0;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
f = fopen("/etc/resolv.conf", "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
r = 0;
|
||||
else
|
||||
r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m");
|
||||
goto clear;
|
||||
}
|
||||
|
||||
if (fstat(fileno(f), &st) < 0) {
|
||||
r = log_error_errno(errno, "Failed to stat open file: %m");
|
||||
goto clear;
|
||||
}
|
||||
|
||||
dns_server_mark_all(m->dns_servers);
|
||||
dns_search_domain_mark_all(m->search_domains);
|
||||
|
||||
FOREACH_LINE(line, f, r = -errno; goto clear) {
|
||||
const char *a;
|
||||
char *l;
|
||||
|
||||
l = strstrip(line);
|
||||
if (*l == '#' || *l == ';')
|
||||
continue;
|
||||
|
||||
a = first_word(l, "nameserver");
|
||||
if (a) {
|
||||
r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
a = first_word(l, "domain");
|
||||
if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
|
||||
a = first_word(l, "search");
|
||||
if (a) {
|
||||
r = manager_parse_search_domains_and_warn(m, a);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Flush out all servers and search domains that are still
|
||||
* marked. Those are then ones that didn't appear in the new
|
||||
* /etc/resolv.conf */
|
||||
dns_server_unlink_marked(m->dns_servers);
|
||||
dns_search_domain_unlink_marked(m->search_domains);
|
||||
|
||||
/* Whenever /etc/resolv.conf changes, start using the first
|
||||
* DNS server of it. This is useful to deal with broken
|
||||
* network managing implementations (like NetworkManager),
|
||||
* that when connecting to a VPN place both the VPN DNS
|
||||
* servers and the local ones in /etc/resolv.conf. Without
|
||||
* resetting the DNS server to use back to the first entry we
|
||||
* will continue to use the local one thus being unable to
|
||||
* resolve VPN domains. */
|
||||
manager_set_dns_server(m, m->dns_servers);
|
||||
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
dns_server_unlink_all(m->dns_servers);
|
||||
dns_search_domain_unlink_all(m->search_domains);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(count);
|
||||
|
||||
r = in_addr_to_string(s->family, &s->address, &t);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*count == MAXNS)
|
||||
fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
|
||||
(*count) ++;
|
||||
|
||||
fprintf(f, "nameserver %s\n", t);
|
||||
}
|
||||
|
||||
static void write_resolv_conf_search(
|
||||
const char *domain,
|
||||
FILE *f,
|
||||
unsigned *count,
|
||||
unsigned *length) {
|
||||
|
||||
assert(domain);
|
||||
assert(f);
|
||||
assert(length);
|
||||
|
||||
if (*count >= MAXDNSRCH ||
|
||||
*length + strlen(domain) > 256) {
|
||||
if (*count == MAXDNSRCH)
|
||||
fputs(" # Too many search domains configured, remaining ones ignored.", f);
|
||||
if (*length <= 256)
|
||||
fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
(*length) += strlen(domain);
|
||||
(*count) ++;
|
||||
|
||||
fputc(' ', f);
|
||||
fputs(domain, f);
|
||||
}
|
||||
|
||||
static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
|
||||
Iterator i;
|
||||
|
||||
fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
|
||||
"# Third party programs must not access this file directly, but\n"
|
||||
"# only through the symlink at /etc/resolv.conf. To manage\n"
|
||||
"# resolv.conf(5) in a different way, replace the symlink by a\n"
|
||||
"# static file or a different symlink.\n\n", f);
|
||||
|
||||
if (ordered_set_isempty(dns))
|
||||
fputs("# No DNS servers known.\n", f);
|
||||
else {
|
||||
unsigned count = 0;
|
||||
DnsServer *s;
|
||||
|
||||
ORDERED_SET_FOREACH(s, dns, i)
|
||||
write_resolv_conf_server(s, f, &count);
|
||||
}
|
||||
|
||||
if (!ordered_set_isempty(domains)) {
|
||||
unsigned length = 0, count = 0;
|
||||
char *domain;
|
||||
|
||||
fputs("search", f);
|
||||
ORDERED_SET_FOREACH(domain, domains, i)
|
||||
write_resolv_conf_search(domain, f, &count, &length);
|
||||
fputs("\n", f);
|
||||
}
|
||||
|
||||
return fflush_and_check(f);
|
||||
}
|
||||
|
||||
int manager_write_resolv_conf(Manager *m) {
|
||||
|
||||
#define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
|
||||
|
||||
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
|
||||
_cleanup_free_ char *temp_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Read the system /etc/resolv.conf first */
|
||||
manager_read_resolv_conf(m);
|
||||
|
||||
/* Add the full list to a set, to filter out duplicates */
|
||||
r = manager_compile_dns_servers(m, &dns);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_compile_search_domains(m, &domains);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fchmod(fileno(f), 0644);
|
||||
|
||||
r = write_resolv_conf_contents(f, dns, domains);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
(void) unlink(PRIVATE_RESOLV_CONF);
|
||||
(void) unlink(temp_path);
|
||||
return r;
|
||||
}
|
27
src/resolve/resolved-resolv-conf.h
Normal file
27
src/resolve/resolved-resolv-conf.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2014 Tom Gundersen <teg@jklm.no>
|
||||
|
||||
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/>.
|
||||
***/
|
||||
|
||||
#include "resolved-manager.h"
|
||||
|
||||
int manager_read_resolv_conf(Manager *m);
|
||||
int manager_write_resolv_conf(Manager *m);
|
@ -26,6 +26,7 @@
|
||||
#include "mkdir.h"
|
||||
#include "resolved-conf.h"
|
||||
#include "resolved-manager.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "selinux-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "user-util.h"
|
||||
@ -81,8 +82,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
r = manager_parse_config_file(m);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse configuration file: %m");
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse configuration file: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = manager_start(m);
|
||||
if (r < 0) {
|
||||
|
@ -14,4 +14,5 @@
|
||||
[Resolve]
|
||||
#DNS=
|
||||
#FallbackDNS=@DNS_SERVERS@
|
||||
#Domains=
|
||||
#LLMNR=yes
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz) {
|
||||
@ -181,30 +182,31 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
||||
return r;
|
||||
}
|
||||
|
||||
int dns_label_escape(const char *p, size_t l, char **ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
|
||||
char *q;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
if (l > DNS_LABEL_MAX)
|
||||
return -EINVAL;
|
||||
if (sz < 1)
|
||||
return -ENOSPC;
|
||||
|
||||
s = malloc(l * 4 + 1);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
assert(p);
|
||||
assert(dest);
|
||||
|
||||
q = s;
|
||||
q = dest;
|
||||
while (l > 0) {
|
||||
|
||||
if (*p == '.' || *p == '\\') {
|
||||
|
||||
if (sz < 3)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Dot or backslash */
|
||||
*(q++) = '\\';
|
||||
*(q++) = *p;
|
||||
|
||||
sz -= 2;
|
||||
|
||||
} else if (*p == '_' ||
|
||||
*p == '-' ||
|
||||
(*p >= '0' && *p <= '9') ||
|
||||
@ -212,15 +214,27 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
|
||||
(*p >= 'A' && *p <= 'Z')) {
|
||||
|
||||
/* Proper character */
|
||||
|
||||
if (sz < 2)
|
||||
return -ENOSPC;
|
||||
|
||||
*(q++) = *p;
|
||||
sz -= 1;
|
||||
|
||||
} else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
|
||||
|
||||
/* Everything else */
|
||||
|
||||
if (sz < 5)
|
||||
return -ENOSPC;
|
||||
|
||||
*(q++) = '\\';
|
||||
*(q++) = '0' + (char) ((uint8_t) *p / 100);
|
||||
*(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
|
||||
*(q++) = '0' + (char) ((uint8_t) *p % 10);
|
||||
|
||||
sz -= 4;
|
||||
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
@ -229,8 +243,28 @@ int dns_label_escape(const char *p, size_t l, char **ret) {
|
||||
}
|
||||
|
||||
*q = 0;
|
||||
return (int) (q - dest);
|
||||
}
|
||||
|
||||
int dns_label_escape_new(const char *p, size_t l, char **ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
if (l > DNS_LABEL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
s = new(char, DNS_LABEL_ESCAPED_MAX);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = s;
|
||||
r = q - s;
|
||||
s = NULL;
|
||||
|
||||
return r;
|
||||
@ -350,28 +384,32 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
|
||||
if (k > 0)
|
||||
r = k;
|
||||
|
||||
r = dns_label_escape(label, r, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (_ret) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!first)
|
||||
ret[n++] = '.';
|
||||
else
|
||||
first = false;
|
||||
r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memcpy(ret + n, t, r);
|
||||
if (!first)
|
||||
ret[n] = '.';
|
||||
} else {
|
||||
char escaped[DNS_LABEL_ESCAPED_MAX];
|
||||
|
||||
r = dns_label_escape(label, r, escaped, sizeof(escaped));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
n++;
|
||||
else
|
||||
first = false;
|
||||
|
||||
n += r;
|
||||
}
|
||||
|
||||
if (n > DNS_NAME_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (_ret) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + 1))
|
||||
return -ENOMEM;
|
||||
@ -752,36 +790,27 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_name_root(const char *name) {
|
||||
char label[DNS_LABEL_MAX+1];
|
||||
int r;
|
||||
bool dns_name_is_root(const char *name) {
|
||||
|
||||
assert(name);
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* There are exactly two ways to encode the root domain name:
|
||||
* as empty string, or with a single dot. */
|
||||
|
||||
return r == 0 && *name == 0;
|
||||
return STR_IN_SET(name, "", ".");
|
||||
}
|
||||
|
||||
int dns_name_single_label(const char *name) {
|
||||
bool dns_name_is_single_label(const char *name) {
|
||||
char label[DNS_LABEL_MAX+1];
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 0;
|
||||
if (r <= 0)
|
||||
return false;
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r == 0 && *name == 0;
|
||||
return dns_name_is_root(name);
|
||||
}
|
||||
|
||||
/* Encode a domain name according to RFC 1035 Section 3.1 */
|
||||
@ -846,12 +875,12 @@ static bool srv_type_label_is_valid(const char *label, size_t n) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int dns_srv_type_verify(const char *name) {
|
||||
bool dns_srv_type_is_valid(const char *name) {
|
||||
unsigned c = 0;
|
||||
int r;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
for (;;) {
|
||||
char label[DNS_LABEL_MAX];
|
||||
@ -859,18 +888,16 @@ int dns_srv_type_verify(const char *name) {
|
||||
/* This more or less implements RFC 6335, Section 5.1 */
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
if (r == -EINVAL)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return false;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (c >= 2)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (!srv_type_label_is_valid(label, r))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
c++;
|
||||
}
|
||||
@ -902,14 +929,15 @@ bool dns_service_name_is_valid(const char *name) {
|
||||
}
|
||||
|
||||
int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
|
||||
_cleanup_free_ char *escaped = NULL, *n = NULL;
|
||||
char escaped[DNS_LABEL_ESCAPED_MAX];
|
||||
_cleanup_free_ char *n = NULL;
|
||||
int r;
|
||||
|
||||
assert(type);
|
||||
assert(domain);
|
||||
assert(ret);
|
||||
|
||||
if (!dns_srv_type_verify(type))
|
||||
if (!dns_srv_type_is_valid(type))
|
||||
return -EINVAL;
|
||||
|
||||
if (!name)
|
||||
@ -918,7 +946,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
|
||||
if (!dns_service_name_is_valid(name))
|
||||
return -EINVAL;
|
||||
|
||||
r = dns_label_escape(name, strlen(name), &escaped);
|
||||
r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -26,11 +26,12 @@
|
||||
#include "in-addr-util.h"
|
||||
|
||||
#define DNS_LABEL_MAX 63
|
||||
#define DNS_NAME_MAX 255
|
||||
#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz);
|
||||
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
|
||||
int dns_label_escape(const char *p, size_t l, char **ret);
|
||||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
|
||||
int dns_label_escape_new(const char *p, size_t l, char **ret);
|
||||
|
||||
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
|
||||
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
|
||||
@ -67,16 +68,13 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
|
||||
int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
|
||||
int dns_name_address(const char *p, int *family, union in_addr_union *a);
|
||||
|
||||
int dns_name_root(const char *name);
|
||||
int dns_name_single_label(const char *name);
|
||||
bool dns_name_is_root(const char *name);
|
||||
bool dns_name_is_single_label(const char *name);
|
||||
|
||||
int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len);
|
||||
|
||||
int dns_srv_type_verify(const char *name);
|
||||
|
||||
bool dns_srv_type_is_valid(const char *name);
|
||||
bool dns_service_name_is_valid(const char *name);
|
||||
|
||||
int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
|
||||
int dns_service_split(const char *joined, char **name, char **type, char **domain);
|
||||
|
||||
int dns_name_replace_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
|
||||
|
@ -126,7 +126,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
|
||||
_cleanup_free_ char *t = NULL;
|
||||
int r;
|
||||
|
||||
r = dns_label_escape(what, l, &t);
|
||||
r = dns_label_escape_new(what, l, &t);
|
||||
assert_se(r == ret);
|
||||
|
||||
if (r < 0)
|
||||
@ -246,21 +246,21 @@ static void test_dns_name_endswith(void) {
|
||||
test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
|
||||
}
|
||||
|
||||
static void test_dns_name_root(void) {
|
||||
assert_se(dns_name_root("") == true);
|
||||
assert_se(dns_name_root(".") == true);
|
||||
assert_se(dns_name_root("xxx") == false);
|
||||
assert_se(dns_name_root("xxx.") == false);
|
||||
assert_se(dns_name_root("..") == -EINVAL);
|
||||
static void test_dns_name_is_root(void) {
|
||||
assert_se(dns_name_is_root(""));
|
||||
assert_se(dns_name_is_root("."));
|
||||
assert_se(!dns_name_is_root("xxx"));
|
||||
assert_se(!dns_name_is_root("xxx."));
|
||||
assert_se(!dns_name_is_root(".."));
|
||||
}
|
||||
|
||||
static void test_dns_name_single_label(void) {
|
||||
assert_se(dns_name_single_label("") == false);
|
||||
assert_se(dns_name_single_label(".") == false);
|
||||
assert_se(dns_name_single_label("..") == -EINVAL);
|
||||
assert_se(dns_name_single_label("x") == true);
|
||||
assert_se(dns_name_single_label("x.") == true);
|
||||
assert_se(dns_name_single_label("xx.yy") == false);
|
||||
static void test_dns_name_is_single_label(void) {
|
||||
assert_se(!dns_name_is_single_label(""));
|
||||
assert_se(!dns_name_is_single_label("."));
|
||||
assert_se(!dns_name_is_single_label(".."));
|
||||
assert_se(dns_name_is_single_label("x"));
|
||||
assert_se(dns_name_is_single_label("x."));
|
||||
assert_se(!dns_name_is_single_label("xx.yy"));
|
||||
}
|
||||
|
||||
static void test_dns_name_reverse_one(const char *address, const char *name) {
|
||||
@ -327,27 +327,27 @@ static void test_dns_service_name_is_valid(void) {
|
||||
assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters"));
|
||||
}
|
||||
|
||||
static void test_dns_srv_type_verify(void) {
|
||||
static void test_dns_srv_type_is_valid(void) {
|
||||
|
||||
assert_se(dns_srv_type_verify("_http._tcp") > 0);
|
||||
assert_se(dns_srv_type_verify("_foo-bar._tcp") > 0);
|
||||
assert_se(dns_srv_type_verify("_w._udp") > 0);
|
||||
assert_se(dns_srv_type_verify("_a800._tcp") > 0);
|
||||
assert_se(dns_srv_type_verify("_a-800._tcp") > 0);
|
||||
assert_se(dns_srv_type_is_valid("_http._tcp"));
|
||||
assert_se(dns_srv_type_is_valid("_foo-bar._tcp"));
|
||||
assert_se(dns_srv_type_is_valid("_w._udp"));
|
||||
assert_se(dns_srv_type_is_valid("_a800._tcp"));
|
||||
assert_se(dns_srv_type_is_valid("_a-800._tcp"));
|
||||
|
||||
assert_se(dns_srv_type_verify(NULL) == 0);
|
||||
assert_se(dns_srv_type_verify("") == 0);
|
||||
assert_se(dns_srv_type_verify("x") == 0);
|
||||
assert_se(dns_srv_type_verify("_foo") == 0);
|
||||
assert_se(dns_srv_type_verify("_tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_") == 0);
|
||||
assert_se(dns_srv_type_verify("_foo.") == 0);
|
||||
assert_se(dns_srv_type_verify("_föo._tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_f\no._tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_800._tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_-800._tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_-foo._tcp") == 0);
|
||||
assert_se(dns_srv_type_verify("_piep._foo._udp") == 0);
|
||||
assert_se(!dns_srv_type_is_valid(NULL));
|
||||
assert_se(!dns_srv_type_is_valid(""));
|
||||
assert_se(!dns_srv_type_is_valid("x"));
|
||||
assert_se(!dns_srv_type_is_valid("_foo"));
|
||||
assert_se(!dns_srv_type_is_valid("_tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_"));
|
||||
assert_se(!dns_srv_type_is_valid("_foo."));
|
||||
assert_se(!dns_srv_type_is_valid("_föo._tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_f\no._tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_800._tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_-800._tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_-foo._tcp"));
|
||||
assert_se(!dns_srv_type_is_valid("_piep._foo._udp"));
|
||||
}
|
||||
|
||||
static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) {
|
||||
@ -436,14 +436,14 @@ int main(int argc, char *argv[]) {
|
||||
test_dns_name_equal();
|
||||
test_dns_name_endswith();
|
||||
test_dns_name_between();
|
||||
test_dns_name_root();
|
||||
test_dns_name_single_label();
|
||||
test_dns_name_is_root();
|
||||
test_dns_name_is_single_label();
|
||||
test_dns_name_reverse();
|
||||
test_dns_name_concat();
|
||||
test_dns_name_is_valid();
|
||||
test_dns_name_to_wire_format();
|
||||
test_dns_service_name_is_valid();
|
||||
test_dns_srv_type_verify();
|
||||
test_dns_srv_type_is_valid();
|
||||
test_dns_service_join();
|
||||
test_dns_service_split();
|
||||
test_dns_name_change_suffix();
|
||||
|
Loading…
Reference in New Issue
Block a user