Merge pull request #3549 from poettering/resolved-more

resolved: more fixes, among them "systemctl-resolve --status" to see DNS configuration in effect, and a local DNS stub listener on 127.0.0.53
This commit is contained in:
Tom Gundersen 2016-06-24 01:26:25 +02:00 committed by GitHub
commit a2c28c6451
52 changed files with 2385 additions and 207 deletions

View file

@ -125,6 +125,7 @@ dist_systemunit_DATA_busnames =
dist_sysusers_DATA =
check_PROGRAMS =
check_DATA =
dist_rootlibexec_DATA =
tests=
manual_tests =
TEST_EXTENSIONS = .py
@ -5147,7 +5148,7 @@ systemd_export_LDADD = \
$(ZLIB_LIBS) \
-lbz2
dist_rootlibexec_DATA = \
dist_rootlibexec_DATA += \
src/import/import-pubring.gpg
nodist_systemunit_DATA += \
@ -5259,6 +5260,8 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-dns-stream.c \
src/resolve/resolved-dns-trust-anchor.h \
src/resolve/resolved-dns-trust-anchor.c \
src/resolve/resolved-dns-stub.h \
src/resolve/resolved-dns-stub.c \
src/resolve/resolved-etc-hosts.h \
src/resolve/resolved-etc-hosts.c \
src/shared/gcrypt-util.c \
@ -5411,6 +5414,9 @@ EXTRA_DIST += \
units/systemd-resolved.service.m4.in \
src/resolve/resolved.conf.in
dist_rootlibexec_DATA += \
src/resolve/resolv.conf
# ------------------------------------------------------------------------------
if ENABLE_NETWORKD
rootlibexec_PROGRAMS += \

View file

@ -294,8 +294,15 @@
<listitem><para>Flushes all DNS resource record caches the service maintains locally.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--status</option></term>
<listitem><para>Shows the global and per-link DNS settings in currently in effect.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist>
</refsect1>

View file

@ -58,27 +58,45 @@
<para><command>systemd-resolved</command> is a system service that provides network name resolution to local
applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR resolver and
responder. In addition it maintains the <filename>/run/systemd/resolve/resolv.conf</filename> file for
compatibility with traditional Linux programs. This file may be symlinked from
<filename>/etc/resolv.conf</filename>.</para>
responder. Local applications may submit network name resolution requests via three interfaces:</para>
<para>The glibc NSS module
<citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry> is required to
permit glibc's NSS resolver functions to resolve host names via <command>systemd-resolved</command>.</para>
<itemizedlist>
<listitem><para>The native, fully-featured API <command>systemd-resolved</command> exposes on the bus. See the
<ulink url="http://www.freedesktop.org/wiki/Software/systemd/resolved">API Documentation</ulink> for
details. Usage of this API is generally recommended to clients as it is asynchronous and fully featured (for
example, properly returns DNSSEC validation status and interface scope for addresses as necessary for supporting
link-local networking).</para></listitem>
<para>The DNS servers contacted are determined from the global
settings in <filename>/etc/systemd/resolved.conf</filename>, the
per-link static settings in <filename>/etc/systemd/network/*.network</filename> files,
and the per-link dynamic settings received over DHCP. See
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. To improve compatibility,
<filename>/etc/resolv.conf</filename> is read in order to discover
configured system DNS servers, but only if it is not a symlink
to <filename>/run/systemd/resolve/resolv.conf</filename> (see above).</para>
<listitem><para>The glibc
<citerefentry><refentrytitle>getaddrinfo</refentrytitle><manvolnum>3</manvolnum></citerefentry> API (as defined
by <ulink url="https://tools.ietf.org/html/rfc3493">RFC3493</ulink>) and its related resolver functions,
including <citerefentry><refentrytitle>gethostbyname</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
API is widely supported, including beyond the Linux platform. In its current form it does not expose DNSSEC
validation status information however, and is synchronous only. This API is backed by the glibc Name Service
Switch (<citerefentry><refentrytitle>nss</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Usage of the
glibc NSS module <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is required in order to allow glibc's NSS resolver functions to resolve host names via
<command>systemd-resolved</command>.</para></listitem>
<para><command>systemd-resolved</command> synthesizes DNS RRs for the following cases:</para>
<listitem><para>Additionally, <command>systemd-resolved</command> provides a local DNS stub listener on IP
address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly, bypassing any local
API may be directed to this stub, in order to connect them <command>systemd-resolved</command>. Note however that
it is strongly recommended that local programs use the glibc NSS or bus APIs instead (as described above), as
various network resolution concepts (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped to
the unicast DNS protocol.</para></listitem>
</itemizedlist>
<para>The DNS servers contacted are determined from the global settings in
<filename>/etc/systemd/resolved.conf</filename>, the per-link static settings in
<filename>/etc/systemd/network/*.network</filename> files, the per-link dynamic settings received over DHCP and any
DNS server information made available by other system services. See
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details
about systemd's own configuration files for DNS servers. To improve compatibility,
<filename>/etc/resolv.conf</filename> is read in order to discover configured system DNS servers, but only if it is
not a symlink to <filename>/run/systemd/resolve/resolv.conf</filename> (see below).</para>
<para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following cases:</para>
<itemizedlist>
<listitem><para>The local, configured hostname is resolved to
@ -137,14 +155,45 @@
per-interface domains are exclusively routed to the matching
interfaces.</para>
<para>Note that <filename>/run/systemd/resolve/resolv.conf</filename> should not be used directly by applications,
but only through a symlink from <filename>/etc/resolv.conf</filename>.</para>
<para>See the <ulink url="http://www.freedesktop.org/wiki/Software/systemd/resolved"> resolved D-Bus API
Documentation</ulink> for information about the APIs <filename>systemd-resolved</filename> provides.</para>
</refsect1>
<refsect1>
<title><filename>/etc/resolv.conf</filename></title>
<para>Three modes of handling <filename>/etc/resolv.conf</filename> (see
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>) are
supported:</para>
<itemizedlist>
<listitem><para>A static file <filename>/usr/lib/systemd/resolv.conf</filename> is provided that lists
the 127.0.0.53 DNS stub (see above) as only DNS server. This file may be symlinked from
<filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs to
<command>systemd-resolved</command>. This mode of operation is recommended.</para></listitem>
<listitem><para><command>systemd-resolved</command> maintains the
<filename>/run/systemd/resolve/resolv.conf</filename> file for compatibility with traditional Linux
programs. This file may be symlinked from <filename>/etc/resolv.conf</filename> and is always kept up-to-date,
containing information about all known DNS servers. Note the file format's limitations: it does not know a
concept of per-interface DNS servers and hence only contains system-wide DNS server definitions. Note that
<filename>/run/systemd/resolve/resolv.conf</filename> should not be used directly by applications, but only
through a symlink from <filename>/etc/resolv.conf</filename>. If this mode of operation is used local clients
that bypass any local DNS API will also bypass <command>systemd-resolved</command> and will talk directly to the
known DNS servers.</para> </listitem>
<listitem><para>Alternatively, <filename>/etc/resolv.conf</filename> may be managed by other packages, in which
case <command>systemd-resolved</command> will read it for DNS configuration data. In this mode of operation
<command>systemd-resolved</command> is consumer rather than provider of this configuration
file. </para></listitem>
</itemizedlist>
<para>Note that the selected mode of operation for this file is detected fully automatically, depending on whether
<filename>/etc/resolv.conf</filename> is a symlink to <filename>/run/systemd/resolve/resolv.conf</filename> or
lists 127.0.0.53 as DNS server.</para>
</refsect1>
<refsect1>
<title>Signals</title>

View file

@ -50,6 +50,23 @@ Bitmap *bitmap_new(void) {
return new0(Bitmap, 1);
}
Bitmap *bitmap_copy(Bitmap *b) {
Bitmap *ret;
ret = bitmap_new();
if (!ret)
return NULL;
ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps);
if (!ret->bitmaps) {
free(ret);
return NULL;
}
ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps;
return ret;
}
void bitmap_free(Bitmap *b) {
if (!b)
return;

View file

@ -27,10 +27,9 @@
typedef struct Bitmap Bitmap;
Bitmap *bitmap_new(void);
void bitmap_free(Bitmap *b);
Bitmap *bitmap_copy(Bitmap *b);
int bitmap_ensure_allocated(Bitmap **b);
void bitmap_free(Bitmap *b);
int bitmap_set(Bitmap *b, unsigned n);
void bitmap_unset(Bitmap *b, unsigned n);

View file

@ -1764,6 +1764,9 @@ void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
int set_consume(Set *s, void *value) {
int r;
assert(s);
assert(value);
r = set_put(s, value);
if (r <= 0)
free(value);
@ -1791,6 +1794,8 @@ int set_put_strdupv(Set *s, char **l) {
int n = 0, r;
char **i;
assert(s);
STRV_FOREACH(i, l) {
r = set_put_strdup(s, *i);
if (r < 0)
@ -1801,3 +1806,23 @@ int set_put_strdupv(Set *s, char **l) {
return n;
}
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) {
const char *p = v;
int r;
assert(s);
assert(v);
for (;;) {
char *word;
r = extract_first_word(&p, &word, separators, flags);
if (r <= 0)
return r;
r = set_consume(s, word);
if (r < 0)
return r;
}
}

View file

@ -553,7 +553,7 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
if (errno == EINTR)
continue;
return -errno;
return negative_errno();
}
return 0;

View file

@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "extract-word.h"
#include "hashmap.h"
#include "macro.h"
@ -122,6 +123,7 @@ static inline char **set_get_strv(Set *s) {
int set_consume(Set *s, void *value);
int set_put_strdup(Set *s, const char *p);
int set_put_strdupv(Set *s, char **l);
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
#define SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )

View file

@ -48,6 +48,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
scope type name##_from_string(const char *s) { \
int b; \
if (!s) \
return -1; \
b = parse_boolean(s); \
if (b == 0) \
return (type) 0; \

View file

@ -481,7 +481,7 @@ static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags
if (ptr != MAP_FAILED)
break;
if (errno != ENOMEM)
return -errno;
return negative_errno();
r = make_room(m);
if (r < 0)
@ -571,7 +571,7 @@ static int add_mmap(
return 1;
outofmem:
munmap(d, wsize);
(void) munmap(d, wsize);
return -ENOMEM;
}

View file

@ -290,7 +290,7 @@ typedef struct SessionStatusInfo {
char *seat;
char *tty;
char *display;
bool remote;
int remote;
char *remote_host;
char *remote_user;
char *service;
@ -304,7 +304,7 @@ typedef struct SessionStatusInfo {
typedef struct UserStatusInfo {
uid_t uid;
bool linger;
int linger;
char *name;
struct dual_timestamp timestamp;
char *state;

View file

@ -58,7 +58,7 @@ static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint
struct bridge_vlan_info br_vlan;
int i, j, k, r, done, cnt;
uint16_t begin, end;
bool untagged;
bool untagged = false;
assert(link);
assert(req);

View file

@ -52,7 +52,7 @@ Network.DNS, config_parse_strv,
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, offsetof(Network, dnssec_negative_trust_anchors)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, 0
Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)

View file

@ -96,6 +96,15 @@ bool dns_type_is_valid_query(uint16_t type) {
DNS_TYPE_RRSIG);
}
bool dns_type_is_zone_transer(uint16_t type) {
/* Zone transfers, either normal or incremental */
return IN_SET(type,
DNS_TYPE_AXFR,
DNS_TYPE_IXFR);
}
bool dns_type_is_valid_rr(uint16_t type) {
/* The types valid as RR in packets (but not necessarily

View file

@ -136,6 +136,7 @@ bool dns_type_is_obsolete(uint16_t type);
bool dns_type_may_wildcard(uint16_t type);
bool dns_type_apex_only(uint16_t type);
bool dns_type_needs_authentication(uint16_t type);
bool dns_type_is_zone_transer(uint16_t type);
int dns_type_to_af(uint16_t type);
bool dns_class_is_pseudo(uint16_t class);

11
src/resolve/resolv.conf Normal file
View file

@ -0,0 +1,11 @@
# This is a static resolv.conf file for connecting local clients to
# systemd-resolved via its DNS stub listener on 127.0.0.53.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
nameserver 127.0.0.53

View file

@ -21,17 +21,21 @@
#include <net/if.h>
#include "sd-bus.h"
#include "sd-netlink.h"
#include "af-list.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
#include "escape.h"
#include "in-addr-util.h"
#include "gcrypt-util.h"
#include "in-addr-util.h"
#include "netlink-util.h"
#include "pager.h"
#include "parse-util.h"
#include "resolved-def.h"
#include "resolved-dns-packet.h"
#include "strv.h"
#include "terminal-util.h"
#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
@ -42,6 +46,7 @@ static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
static bool arg_no_pager = false;
typedef enum ServiceFamily {
SERVICE_FAMILY_TCP,
@ -67,6 +72,7 @@ static enum {
MODE_STATISTICS,
MODE_RESET_STATISTICS,
MODE_FLUSH_CACHES,
MODE_STATUS,
} arg_mode = MODE_RESOLVE_HOST;
static ServiceFamily service_family_from_string(const char *s) {
@ -1031,6 +1037,472 @@ static int flush_caches(sd_bus *bus) {
return 0;
}
static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
assert(bus);
assert(member);
assert(m);
assert(l);
r = sd_bus_message_enter_container(m, 'a', "(iay)");
if (r < 0)
return r;
for (;;) {
const void *a;
char *pretty;
int family;
size_t sz;
r = sd_bus_message_enter_container(m, 'r', "iay");
if (r < 0)
return r;
if (r == 0)
break;
r = sd_bus_message_read(m, "i", &family);
if (r < 0)
return r;
r = sd_bus_message_read_array(m, 'y', &a, &sz);
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
if (!IN_SET(family, AF_INET, AF_INET6)) {
log_debug("Unexpected family, ignoring.");
continue;
}
if (sz != FAMILY_ADDRESS_SIZE(family)) {
log_debug("Address size mismatch, ignoring.");
continue;
}
r = in_addr_to_string(family, a, &pretty);
if (r < 0)
return r;
r = strv_consume(l, pretty);
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
assert(bus);
assert(member);
assert(m);
assert(l);
r = sd_bus_message_enter_container(m, 'a', "(sb)");
if (r < 0)
return r;
for (;;) {
const char *domain;
int route_only;
char *pretty;
r = sd_bus_message_read(m, "(sb)", &domain, &route_only);
if (r < 0)
return r;
if (r == 0)
break;
if (route_only)
pretty = strappend("~", domain);
else
pretty = strdup(domain);
if (!pretty)
return -ENOMEM;
r = strv_consume(l, pretty);
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empty_line) {
struct link_info {
uint64_t scopes_mask;
char *llmnr;
char *mdns;
char *dnssec;
char **dns;
char **domains;
char **ntas;
int dnssec_supported;
} link_info = {};
static const struct bus_properties_map property_map[] = {
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
{ "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) },
{ "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) },
{ "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) },
{ "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) },
{ "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) },
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) },
{ "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) },
{}
};
_cleanup_free_ char *ifi = NULL, *p = NULL;
char ifname[IF_NAMESIZE] = "";
char **i;
int r;
assert(bus);
assert(ifindex > 0);
assert(empty_line);
if (!name) {
if (!if_indextoname(ifindex, ifname))
return log_error_errno(errno, "Failed to resolve interface name for %i: %m", ifindex);
name = ifname;
}
if (asprintf(&ifi, "%i", ifindex) < 0)
return log_oom();
r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p);
if (r < 0)
return log_oom();
r = bus_map_all_properties(bus,
"org.freedesktop.resolve1",
p,
property_map,
&link_info);
if (r < 0) {
log_error_errno(r, "Failed to get link data for %i: %m", ifindex);
goto finish;
}
pager_open(arg_no_pager, false);
if (*empty_line)
fputc('\n', stdout);
printf("%sLink %i (%s)%s\n",
ansi_highlight(), ifindex, name, ansi_normal());
if (link_info.scopes_mask == 0)
printf(" Current Scopes: none\n");
else
printf(" Current Scopes:%s%s%s%s%s\n",
link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "",
link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
printf(" LLMNR setting: %s\n"
"MulticastDNS setting: %s\n"
" DNSSEC setting: %s\n"
" DNSSEC supported: %s\n",
strna(link_info.llmnr),
strna(link_info.mdns),
strna(link_info.dnssec),
yes_no(link_info.dnssec_supported));
STRV_FOREACH(i, link_info.dns) {
printf(" %s %s\n",
i == link_info.dns ? "DNS Server:" : " ",
*i);
}
STRV_FOREACH(i, link_info.domains) {
printf(" %s %s\n",
i == link_info.domains ? "DNS Domain:" : " ",
*i);
}
STRV_FOREACH(i, link_info.ntas) {
printf(" %s %s\n",
i == link_info.ntas ? "DNSSEC NTA:" : " ",
*i);
}
*empty_line = true;
r = 0;
finish:
strv_free(link_info.dns);
strv_free(link_info.domains);
free(link_info.llmnr);
free(link_info.mdns);
free(link_info.dnssec);
strv_free(link_info.ntas);
return r;
}
static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
assert(bus);
assert(member);
assert(m);
assert(l);
r = sd_bus_message_enter_container(m, 'a', "(iiay)");
if (r < 0)
return r;
for (;;) {
const void *a;
char *pretty;
int family, ifindex;
size_t sz;
r = sd_bus_message_enter_container(m, 'r', "iiay");
if (r < 0)
return r;
if (r == 0)
break;
r = sd_bus_message_read(m, "ii", &ifindex, &family);
if (r < 0)
return r;
r = sd_bus_message_read_array(m, 'y', &a, &sz);
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
if (ifindex != 0) /* only show the global ones here */
continue;
if (!IN_SET(family, AF_INET, AF_INET6)) {
log_debug("Unexpected family, ignoring.");
continue;
}
if (sz != FAMILY_ADDRESS_SIZE(family)) {
log_debug("Address size mismatch, ignoring.");
continue;
}
r = in_addr_to_string(family, a, &pretty);
if (r < 0)
return r;
r = strv_consume(l, pretty);
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
char ***l = userdata;
int r;
assert(bus);
assert(member);
assert(m);
assert(l);
r = sd_bus_message_enter_container(m, 'a', "(isb)");
if (r < 0)
return r;
for (;;) {
const char *domain;
int route_only, ifindex;
char *pretty;
r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only);
if (r < 0)
return r;
if (r == 0)
break;
if (ifindex != 0) /* only show the global ones here */
continue;
if (route_only)
pretty = strappend("~", domain);
else
pretty = strdup(domain);
if (!pretty)
return -ENOMEM;
r = strv_consume(l, pretty);
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return 0;
}
static int status_global(sd_bus *bus, bool *empty_line) {
struct global_info {
char **dns;
char **domains;
char **ntas;
} global_info = {};
static const struct bus_properties_map property_map[] = {
{ "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) },
{ "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) },
{ "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) },
{}
};
char **i;
int r;
assert(bus);
assert(empty_line);
r = bus_map_all_properties(bus,
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
property_map,
&global_info);
if (r < 0) {
log_error_errno(r, "Failed to get global data: %m");
goto finish;
}
if (strv_isempty(global_info.dns) && strv_isempty(global_info.domains) && strv_isempty(global_info.ntas)) {
r = 0;
goto finish;
}
pager_open(arg_no_pager, false);
printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
STRV_FOREACH(i, global_info.dns) {
printf(" %s %s\n",
i == global_info.dns ? "DNS Server:" : " ",
*i);
}
STRV_FOREACH(i, global_info.domains) {
printf(" %s %s\n",
i == global_info.domains ? "DNS Domain:" : " ",
*i);
}
strv_sort(global_info.ntas);
STRV_FOREACH(i, global_info.ntas) {
printf(" %s %s\n",
i == global_info.ntas ? "DNSSEC NTA:" : " ",
*i);
}
*empty_line = true;
r = 0;
finish:
strv_free(global_info.dns);
strv_free(global_info.domains);
strv_free(global_info.ntas);
return r;
}
static int status_all(sd_bus *bus) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
sd_netlink_message *i;
bool empty_line = true;
int r;
assert(bus);
r = status_global(bus, &empty_line);
if (r < 0)
return r;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_netlink_message_request_dump(req, true);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_netlink_call(rtnl, req, 0, &reply);
if (r < 0)
return log_error_errno(r, "Failed to enumerate links: %m");
r = 0;
for (i = reply; i; i = sd_netlink_message_next(i)) {
const char *name;
int ifindex, q;
uint16_t type;
q = sd_netlink_message_get_type(i, &type);
if (q < 0)
return rtnl_log_parse_error(q);
if (type != RTM_NEWLINK)
continue;
q = sd_rtnl_message_link_get_ifindex(i, &ifindex);
if (q < 0)
return rtnl_log_parse_error(q);
if (ifindex == LOOPBACK_IFINDEX)
continue;
q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
if (q < 0)
return rtnl_log_parse_error(q);
q = status_ifindex(bus, ifindex, name, &empty_line);
if (q < 0 && r >= 0)
r = q;
}
return r;
}
static void help_protocol_types(void) {
if (arg_legend)
puts("Known protocol types:");
@ -1038,8 +1510,8 @@ static void help_protocol_types(void) {
}
static void help_dns_types(void) {
int i;
const char *t;
int i;
if (arg_legend)
puts("Known DNS RR types:");
@ -1051,8 +1523,8 @@ static void help_dns_types(void) {
}
static void help_dns_classes(void) {
int i;
const char *t;
int i;
if (arg_legend)
puts("Known DNS RR classes:");
@ -1073,6 +1545,7 @@ static void help(void) {
"Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" -4 Resolve IPv4 addresses\n"
" -6 Resolve IPv6 addresses\n"
" -i --interface=INTERFACE Look on interface\n"
@ -1091,6 +1564,7 @@ static void help(void) {
" --legend=BOOL Print headers and additional info (default: yes)\n"
" --statistics Show resolver statistics\n"
" --reset-statistics Reset resolver statistics\n"
" --status Show link and server status\n"
" --flush-caches Flush all local DNS caches\n"
, program_invocation_short_name);
}
@ -1109,7 +1583,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SEARCH,
ARG_STATISTICS,
ARG_RESET_STATISTICS,
ARG_STATUS,
ARG_FLUSH_CACHES,
ARG_NO_PAGER,
};
static const struct option options[] = {
@ -1130,7 +1606,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "search", required_argument, NULL, ARG_SEARCH },
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
{ "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
{ "status", no_argument, NULL, ARG_STATUS },
{ "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
};
@ -1308,6 +1786,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = MODE_FLUSH_CACHES;
break;
case ARG_STATUS:
arg_mode = MODE_STATUS;
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case '?':
return -EINVAL;
@ -1484,8 +1970,38 @@ int main(int argc, char **argv) {
r = flush_caches(bus);
break;
case MODE_STATUS:
if (argc > optind) {
char **ifname;
bool empty_line = false;
r = 0;
STRV_FOREACH(ifname, argv + optind) {
int ifindex, q;
q = parse_ifindex(argv[optind], &ifindex);
if (q < 0) {
ifindex = if_nametoindex(argv[optind]);
if (ifindex <= 0) {
log_error_errno(errno, "Failed to resolve interface name: %s", argv[optind]);
continue;
}
}
q = status_ifindex(bus, ifindex, NULL, &empty_line);
if (q < 0 && r >= 0)
r = q;
}
} else
r = status_all(bus);
break;
}
finish:
pager_close();
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

View file

@ -647,6 +647,8 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (!dns_type_is_valid_query(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
if (dns_type_is_zone_transer(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
if (dns_type_is_obsolete(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
@ -670,6 +672,10 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
/* Let's request that the TTL is fixed up for locally cached entries, after all we return it in the wire format
* blob */
q->clamp_ttl = true;
q->request = sd_bus_message_ref(message);
q->complete = bus_method_resolve_record_complete;
@ -1414,6 +1420,36 @@ static int bus_property_get_dnssec_supported(
return sd_bus_message_append(reply, "b", manager_dnssec_supported(m));
}
static int bus_property_get_ntas(
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;
const char *domain;
Iterator i;
int r;
assert(reply);
assert(m);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
SET_FOREACH(domain, m->trust_anchor.negative_by_name, i) {
r = sd_bus_message_append(reply, "s", domain);
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
DnsScope *s;
@ -1540,6 +1576,7 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 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),

View file

@ -37,6 +37,10 @@ int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char
if (r < 0)
return r;
/* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address))
return 0;
/* Filter out duplicates */
s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
if (s) {

View file

@ -87,6 +87,10 @@ static inline unsigned dns_answer_size(DnsAnswer *a) {
return a ? a->n_rrs : 0;
}
static inline bool dns_answer_isempty(DnsAnswer *a) {
return dns_answer_size(a) <= 0;
}
void dns_answer_dump(DnsAnswer *answer, FILE *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);

View file

@ -790,7 +790,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
return NULL;
}
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
unsigned n = 0;
@ -798,6 +798,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
bool nxdomain = false;
DnsCacheItem *j, *first, *nsec = NULL;
bool have_authenticated = false, have_non_authenticated = false;
usec_t current;
assert(c);
assert(key);
@ -892,11 +893,24 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
if (!answer)
return -ENOMEM;
if (clamp_ttl)
current = now(clock_boottime_or_monotonic());
LIST_FOREACH(by_key, j, first) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
if (!j->rr)
continue;
r = dns_answer_add(answer, j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
if (clamp_ttl) {
rr = dns_resource_record_ref(j->rr);
r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
if (r < 0)
return r;
}
r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
if (r < 0)
return r;
}

View file

@ -40,7 +40,7 @@ void dns_cache_flush(DnsCache *c);
void dns_cache_prune(DnsCache *c);
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **answer, bool *authenticated);
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);

View file

@ -264,6 +264,7 @@ int dns_packet_validate_query(DnsPacket *p) {
switch (p->protocol) {
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_DNS:
/* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
if (DNS_PACKET_QDCOUNT(p) != 1)
return -EBADMSG;
@ -676,13 +677,15 @@ fail:
}
/* Append the OPT pseudo-RR described in RFC6891 */
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start) {
size_t saved_size;
int r;
assert(p);
/* we must never advertise supported packet size smaller than the legacy max */
assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
assert(rcode >= 0);
assert(rcode <= _DNS_RCODE_MAX);
if (p->opt_start != (size_t) -1)
return -EBUSY;
@ -701,13 +704,13 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, si
if (r < 0)
goto fail;
/* maximum udp packet that can be received */
/* class: maximum udp packet that can be received */
r = dns_packet_append_uint16(p, max_udp_size, NULL);
if (r < 0)
goto fail;
/* extended RCODE and VERSION */
r = dns_packet_append_uint16(p, 0, NULL);
r = dns_packet_append_uint16(p, ((uint16_t) rcode & 0x0FF0) << 4, NULL);
if (r < 0)
goto fail;
@ -717,9 +720,8 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, si
goto fail;
/* RDLENGTH */
if (edns0_do) {
/* If DO is on, also append RFC6975 Algorithm data */
if (edns0_do & !DNS_PACKET_QR(p)) {
/* If DO is on and this is not a reply, also append RFC6975 Algorithm data */
static const uint8_t rfc6975[] = {
@ -750,7 +752,6 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, si
r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
} else
r = dns_packet_append_uint16(p, 0, NULL);
if (r < 0)
goto fail;
@ -791,6 +792,7 @@ int dns_packet_truncate_opt(DnsPacket *p) {
}
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
size_t saved_size, rdlength_offset, end, rdlength, rds;
int r;
@ -1134,6 +1136,36 @@ fail:
return r;
}
int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) {
DnsResourceKey *key;
int r;
assert(p);
DNS_QUESTION_FOREACH(key, q) {
r = dns_packet_append_key(p, key, NULL);
if (r < 0)
return r;
}
return 0;
}
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) {
DnsResourceRecord *rr;
int r;
assert(p);
DNS_ANSWER_FOREACH(rr, a) {
r = dns_packet_append_rr(p, rr, NULL, NULL);
if (r < 0)
return r;
}
return 0;
}
int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
assert(p);
@ -2029,8 +2061,10 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
assert(rr->key->type == DNS_TYPE_OPT);
/* Check that the version is 0 */
if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0)
return false;
if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0) {
*rfc6975 = false;
return true; /* if it's not version 0, it's OK, but we will ignore the OPT field contents */
}
p = rr->opt.data;
l = rr->opt.data_size;
@ -2153,16 +2187,27 @@ int dns_packet_extract(DnsPacket *p) {
continue;
}
if (has_rfc6975) {
/* If the OPT RR contains RFC6975 algorithm data, then this is indication that
* the server just copied the OPT it got from us (which contained that data)
* back into the reply. If so, then it doesn't properly support EDNS, as
* RFC6975 makes it very clear that the algorithm data should only be contained
* in questions, never in replies. Crappy Belkin routers copy the OPT data for
* example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
if (DNS_PACKET_QR(p)) {
/* Additional checks for responses */
if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) {
/* If this is a reply and we don't know the EDNS version then something
* is weird... */
log_debug("EDNS version newer that our request, bad server.");
return -EBADMSG;
}
if (has_rfc6975) {
/* If the OPT RR contains RFC6975 algorithm data, then this is indication that
* the server just copied the OPT it got from us (which contained that data)
* back into the reply. If so, then it doesn't properly support EDNS, as
* RFC6975 makes it very clear that the algorithm data should only be contained
* in questions, never in replies. Crappy Belkin routers copy the OPT data for
* example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
}
}
p->opt = dns_resource_record_ref(rr);

View file

@ -118,6 +118,8 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
#define DNS_PACKET_FLAG_TC (UINT16_C(1) << 9)
static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
uint16_t rcode;
@ -126,7 +128,34 @@ static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
else
rcode = 0;
return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15);
return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 0xF);
}
static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) {
/* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */
if (p->opt)
return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class);
return DNS_PACKET_UNICAST_SIZE_MAX;
}
static inline bool DNS_PACKET_DO(DnsPacket *p) {
if (!p->opt)
return false;
return !!(p->opt->ttl & (1U << 15));
}
static inline bool DNS_PACKET_VERSION_SUPPORTED(DnsPacket *p) {
/* Returns true if this packet is in a version we support. Which means either non-EDNS or EDNS(0), but not EDNS
* of any newer versions */
if (!p->opt)
return true;
return DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(p->opt);
}
/* LLMNR defines some bits differently */
@ -182,7 +211,9 @@ int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonica
int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);
int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start);
int dns_packet_append_question(DnsPacket *p, DnsQuestion *q);
int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a);
void dns_packet_truncate(DnsPacket *p, size_t sz);
int dns_packet_truncate_opt(DnsPacket *p);
@ -232,7 +263,8 @@ enum {
DNS_RCODE_BADNAME = 20,
DNS_RCODE_BADALG = 21,
DNS_RCODE_BADTRUNC = 22,
_DNS_RCODE_MAX_DEFINED
_DNS_RCODE_MAX_DEFINED,
_DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */
};
const char* dns_rcode_to_string(int i) _const_;

View file

@ -154,6 +154,7 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
goto gc;
}
t->clamp_ttl = c->query->clamp_ttl;
return 1;
gc:
@ -403,6 +404,16 @@ DnsQuery *dns_query_free(DnsQuery *q) {
sd_bus_message_unref(q->request);
sd_bus_track_unref(q->bus_track);
dns_packet_unref(q->request_dns_packet);
if (q->request_dns_stream) {
/* Detach the stream from our query, in case something else keeps a reference to it. */
q->request_dns_stream->complete = NULL;
q->request_dns_stream->on_packet = NULL;
q->request_dns_stream->query = NULL;
dns_stream_unref(q->request_dns_stream);
}
free(q->request_address_string);
if (q->manager) {
@ -420,7 +431,8 @@ int dns_query_new(
DnsQuery **ret,
DnsQuestion *question_utf8,
DnsQuestion *question_idna,
int ifindex, uint64_t flags) {
int ifindex,
uint64_t flags) {
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
DnsResourceKey *key;

View file

@ -71,6 +71,10 @@ struct DnsQuery {
* family */
bool suppress_unroutable_family;
/* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
bool clamp_ttl;
DnsTransactionState state;
unsigned n_cname_redirects;
@ -95,6 +99,10 @@ struct DnsQuery {
unsigned block_all_complete;
char *request_address_string;
/* DNS stub information */
DnsPacket *request_dns_packet;
DnsStream *request_dns_stream;
/* Completion callback */
void (*complete)(DnsQuery* q);
unsigned block_ready;

View file

@ -56,6 +56,10 @@ static inline unsigned dns_question_size(DnsQuestion *q) {
return q ? q->n_keys : 0;
}
static inline bool dns_question_isempty(DnsQuestion *q) {
return dns_question_size(q) <= 0;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
#define _DNS_QUESTION_FOREACH(u, key, q) \

View file

@ -1532,6 +1532,232 @@ const struct hash_ops dns_resource_record_hash_ops = {
.compare = dns_resource_record_compare_func,
};
DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
DnsResourceRecord *t;
assert(rr);
copy = dns_resource_record_new(rr->key);
if (!copy)
return NULL;
copy->ttl = rr->ttl;
copy->expiry = rr->expiry;
copy->n_skip_labels_signer = rr->n_skip_labels_signer;
copy->n_skip_labels_source = rr->n_skip_labels_source;
copy->unparseable = rr->unparseable;
switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
case DNS_TYPE_SRV:
copy->srv.priority = rr->srv.priority;
copy->srv.weight = rr->srv.weight;
copy->srv.port = rr->srv.port;
copy->srv.name = strdup(rr->srv.name);
if (!copy->srv.name)
return NULL;
break;
case DNS_TYPE_PTR:
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME:
copy->ptr.name = strdup(rr->ptr.name);
if (!copy->ptr.name)
return NULL;
break;
case DNS_TYPE_HINFO:
copy->hinfo.cpu = strdup(rr->hinfo.cpu);
if (!copy->hinfo.cpu)
return NULL;
copy->hinfo.os = strdup(rr->hinfo.os);
if(!copy->hinfo.os)
return NULL;
break;
case DNS_TYPE_TXT:
case DNS_TYPE_SPF:
copy->txt.items = dns_txt_item_copy(rr->txt.items);
if (!copy->txt.items)
return NULL;
break;
case DNS_TYPE_A:
copy->a = rr->a;
break;
case DNS_TYPE_AAAA:
copy->aaaa = rr->aaaa;
break;
case DNS_TYPE_SOA:
copy->soa.mname = strdup(rr->soa.mname);
if (!copy->soa.mname)
return NULL;
copy->soa.rname = strdup(rr->soa.rname);
if (!copy->soa.rname)
return NULL;
copy->soa.serial = rr->soa.serial;
copy->soa.refresh = rr->soa.refresh;
copy->soa.retry = rr->soa.retry;
copy->soa.expire = rr->soa.expire;
copy->soa.minimum = rr->soa.minimum;
break;
case DNS_TYPE_MX:
copy->mx.priority = rr->mx.priority;
copy->mx.exchange = strdup(rr->mx.exchange);
if (!copy->mx.exchange)
return NULL;
break;
case DNS_TYPE_LOC:
copy->loc = rr->loc;
break;
case DNS_TYPE_SSHFP:
copy->sshfp.algorithm = rr->sshfp.algorithm;
copy->sshfp.fptype = rr->sshfp.fptype;
copy->sshfp.fingerprint = memdup(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
if (!copy->sshfp.fingerprint)
return NULL;
copy->sshfp.fingerprint_size = rr->sshfp.fingerprint_size;
break;
case DNS_TYPE_DNSKEY:
copy->dnskey.flags = rr->dnskey.flags;
copy->dnskey.protocol = rr->dnskey.protocol;
copy->dnskey.algorithm = rr->dnskey.algorithm;
copy->dnskey.key = memdup(rr->dnskey.key, rr->dnskey.key_size);
if (!copy->dnskey.key)
return NULL;
copy->dnskey.key_size = rr->dnskey.key_size;
break;
case DNS_TYPE_RRSIG:
copy->rrsig.type_covered = rr->rrsig.type_covered;
copy->rrsig.algorithm = rr->rrsig.algorithm;
copy->rrsig.labels = rr->rrsig.labels;
copy->rrsig.original_ttl = rr->rrsig.original_ttl;
copy->rrsig.expiration = rr->rrsig.expiration;
copy->rrsig.inception = rr->rrsig.inception;
copy->rrsig.key_tag = rr->rrsig.key_tag;
copy->rrsig.signer = strdup(rr->rrsig.signer);
if (!copy->rrsig.signer)
return NULL;
copy->rrsig.signature = memdup(rr->rrsig.signature, rr->rrsig.signature_size);
if (!copy->rrsig.signature)
return NULL;
copy->rrsig.signature_size = rr->rrsig.signature_size;
break;
case DNS_TYPE_NSEC:
copy->nsec.next_domain_name = strdup(rr->nsec.next_domain_name);
if (!copy->nsec.next_domain_name)
return NULL;
copy->nsec.types = bitmap_copy(rr->nsec.types);
if (!copy->nsec.types)
return NULL;
break;
case DNS_TYPE_DS:
copy->ds.key_tag = rr->ds.key_tag;
copy->ds.algorithm = rr->ds.algorithm;
copy->ds.digest_type = rr->ds.digest_type;
copy->ds.digest = memdup(rr->ds.digest, rr->ds.digest_size);
if (!copy->ds.digest)
return NULL;
copy->ds.digest_size = rr->ds.digest_size;
break;
case DNS_TYPE_NSEC3:
copy->nsec3.algorithm = rr->nsec3.algorithm;
copy->nsec3.flags = rr->nsec3.flags;
copy->nsec3.iterations = rr->nsec3.iterations;
copy->nsec3.salt = memdup(rr->nsec3.salt, rr->nsec3.salt_size);
if (!copy->nsec3.salt)
return NULL;
copy->nsec3.salt_size = rr->nsec3.salt_size;
copy->nsec3.next_hashed_name = memdup(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size);
if (!copy->nsec3.next_hashed_name_size)
return NULL;
copy->nsec3.next_hashed_name_size = rr->nsec3.next_hashed_name_size;
copy->nsec3.types = bitmap_copy(rr->nsec3.types);
if (!copy->nsec3.types)
return NULL;
break;
case DNS_TYPE_TLSA:
copy->tlsa.cert_usage = rr->tlsa.cert_usage;
copy->tlsa.selector = rr->tlsa.selector;
copy->tlsa.matching_type = rr->tlsa.matching_type;
copy->tlsa.data = memdup(rr->tlsa.data, rr->tlsa.data_size);
if (!copy->tlsa.data)
return NULL;
copy->tlsa.data_size = rr->tlsa.data_size;
break;
case DNS_TYPE_CAA:
copy->caa.flags = rr->caa.flags;
copy->caa.tag = strdup(rr->caa.tag);
if (!copy->caa.tag)
return NULL;
copy->caa.value = memdup(rr->caa.value, rr->caa.value_size);
if (!copy->caa.value)
return NULL;
copy->caa.value_size = rr->caa.value_size;
break;
case DNS_TYPE_OPT:
default:
copy->generic.data = memdup(rr->generic.data, rr->generic.data_size);
if (!copy->generic.data)
return NULL;
copy->generic.data_size = rr->generic.data_size;
break;
}
t = copy;
copy = NULL;
return t;
}
int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) {
DnsResourceRecord *old_rr, *new_rr;
uint32_t new_ttl;
assert(rr);
old_rr = *rr;
if (old_rr->key->type == DNS_TYPE_OPT)
return -EINVAL;
new_ttl = MIN(old_rr->ttl, max_ttl);
if (new_ttl == old_rr->ttl)
return 0;
if (old_rr->n_ref == 1) {
/* Patch in place */
old_rr->ttl = new_ttl;
return 1;
}
new_rr = dns_resource_record_copy(old_rr);
if (!new_rr)
return -ENOMEM;
new_rr->ttl = new_ttl;
dns_resource_record_unref(*rr);
*rr = new_rr;
return 1;
}
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
DnsTxtItem *n;
@ -1564,6 +1790,25 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
return dns_txt_item_equal(a->items_next, b->items_next);
}
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
DnsTxtItem *i, *copy = NULL, *end = NULL;
LIST_FOREACH(items, i, first) {
DnsTxtItem *j;
j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1);
if (!j) {
dns_txt_item_free_all(copy);
return NULL;
}
LIST_INSERT_AFTER(items, copy, end, j);
end = j;
}
return copy;
}
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",

View file

@ -282,6 +282,13 @@ static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
return rr->wire_format_size - rr->wire_format_rdata_offset;
}
static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) {
assert(rr);
assert(rr->key->type == DNS_TYPE_OPT);
return ((rr->ttl >> 16) & 0xFF) == 0;
}
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
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);
@ -318,6 +325,7 @@ int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const u
int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
const char* dns_resource_record_to_string(DnsResourceRecord *rr);
DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
@ -327,8 +335,11 @@ int dns_resource_record_source(DnsResourceRecord *rr, const char **ret);
int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone);
int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl);
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
void dns_resource_record_hash_func(const void *i, struct siphash *state);

View file

@ -232,7 +232,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
if (fd < 0)
return fd;
r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, p);
if (r < 0)
return r;
@ -257,7 +257,7 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
if (fd < 0)
return fd;
r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, NULL, p);
if (r < 0)
return r;
@ -578,6 +578,7 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in
}
int dns_scope_llmnr_membership(DnsScope *s, bool b) {
assert(s);
if (s->protocol != DNS_PROTOCOL_LLMNR)
return 0;
@ -586,6 +587,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) {
}
int dns_scope_mdns_membership(DnsScope *s, bool b) {
assert(s);
if (s->protocol != DNS_PROTOCOL_MDNS)
return 0;
@ -604,15 +606,14 @@ static int dns_scope_make_reply_packet(
DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
unsigned i;
int r;
assert(s);
assert(ret);
if ((!q || q->n_keys <= 0)
&& (!answer || answer->n_rrs <= 0)
&& (!soa || soa->n_rrs <= 0))
if (dns_question_isempty(q) &&
dns_answer_isempty(answer) &&
dns_answer_isempty(soa))
return -EINVAL;
r = dns_packet_new(&p, s->protocol, 0);
@ -631,35 +632,20 @@ static int dns_scope_make_reply_packet(
0 /* (cd) */,
rcode));
if (q) {
for (i = 0; i < q->n_keys; i++) {
r = dns_packet_append_key(p, q->keys[i], NULL);
if (r < 0)
return r;
}
r = dns_packet_append_question(p, q);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
}
r = dns_packet_append_answer(p, answer);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer));
if (answer) {
for (i = 0; i < answer->n_rrs; i++) {
r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
}
if (soa) {
for (i = 0; i < soa->n_rrs; i++) {
r = dns_packet_append_rr(p, soa->items[i].rr, NULL, NULL);
if (r < 0)
return r;
}
DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
}
r = dns_packet_append_answer(p, soa);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa));
*ret = p;
p = NULL;
@ -668,25 +654,25 @@ static int dns_scope_make_reply_packet(
}
static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
unsigned n;
DnsResourceRecord *rr;
DnsResourceKey *key;
assert(s);
assert(p);
if (p->question)
for (n = 0; n < p->question->n_keys; n++)
dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
if (p->answer)
for (n = 0; n < p->answer->n_rrs; n++)
dns_zone_verify_conflicts(&s->zone, p->answer->items[n].rr->key);
DNS_QUESTION_FOREACH(key, p->question)
dns_zone_verify_conflicts(&s->zone, key);
DNS_ANSWER_FOREACH(rr, p->answer)
dns_zone_verify_conflicts(&s->zone, rr->key);
}
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
int r;
assert(s);
assert(p);
@ -708,7 +694,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
return;
}
@ -718,7 +704,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
assert(p->question->n_keys == 1);
assert(dns_question_size(p->question) == 1);
key = p->question->keys[0];
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
@ -738,9 +724,21 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
if (stream)
if (stream) {
r = dns_stream_write_packet(stream, reply);
else {
if (r < 0) {
log_debug_errno(r, "Failed to enqueue reply packet: %m");
return;
}
/* Let's take an extra reference on this stream, so that it stays around after returning. The reference
* will be dangling until the stream is disconnected, and the default completion handler of the stream
* will then unref the stream and destroy it */
if (DNS_STREAM_QUEUED(stream))
dns_stream_ref(stream);
} else {
int fd;
if (!ratelimit_test(&s->ratelimit))
return;
@ -762,12 +760,11 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
* verified uniqueness for all records. Also see RFC
* 4795, Section 2.7 */
r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
}
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return;
r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, NULL, reply);
if (r < 0) {
log_debug_errno(r, "Failed to send reply packet: %m");
return;
}
}
}

View file

@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "resolved-dns-server.h"
#include "resolved-dns-stub.h"
#include "resolved-resolv-conf.h"
#include "siphash24.h"
#include "string-table.h"
@ -517,7 +518,7 @@ int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeature
else
packet_size = server->received_udp_packet_max;
return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
return dns_packet_append_opt(packet, packet_size, edns_do, 0, NULL);
}
int dns_server_ifindex(const DnsServer *s) {
@ -750,6 +751,19 @@ void manager_next_dns_server(Manager *m) {
manager_set_dns_server(m, m->dns_servers);
}
bool dns_server_address_valid(int family, const union in_addr_union *sa) {
/* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */
if (in_addr_is_null(family, sa))
return false;
if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB))
return false;
return true;
}
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",

View file

@ -141,6 +141,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
DnsServer *manager_get_dns_server(Manager *m);
void manager_next_dns_server(Manager *m);
bool dns_server_address_valid(int family, const union in_addr_union *sa);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
extern const struct hash_ops dns_server_hash_ops;

View file

@ -56,8 +56,8 @@ static int dns_stream_complete(DnsStream *s, int error) {
if (s->complete)
s->complete(s, error);
else
dns_stream_free(s);
else /* the default action if no completion function is set is to close the stream */
dns_stream_unref(s);
return 0;
}
@ -323,10 +323,16 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
return 0;
}
DnsStream *dns_stream_free(DnsStream *s) {
DnsStream *dns_stream_unref(DnsStream *s) {
if (!s)
return NULL;
assert(s->n_ref > 0);
s->n_ref--;
if (s->n_ref > 0)
return NULL;
dns_stream_stop(s);
if (s->manager) {
@ -339,13 +345,23 @@ DnsStream *dns_stream_free(DnsStream *s) {
free(s);
return 0;
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref);
DnsStream *dns_stream_ref(DnsStream *s) {
if (!s)
return NULL;
assert(s->n_ref > 0);
s->n_ref++;
return s;
}
int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
_cleanup_(dns_stream_freep) DnsStream *s = NULL;
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
int r;
assert(m);
@ -358,6 +374,7 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
if (!s)
return -ENOMEM;
s->n_ref = 1;
s->fd = -1;
s->protocol = protocol;

View file

@ -26,8 +26,16 @@ typedef struct DnsStream DnsStream;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
/* Streams are used by three subsystems:
*
* 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP
* 2. The LLMNR logic when accepting a TCP-based lookup
* 3. The DNS stub logic when accepting a TCP-based lookup
*/
struct DnsStream {
Manager *manager;
int n_ref;
DnsProtocol protocol;
@ -50,12 +58,23 @@ struct DnsStream {
int (*on_packet)(DnsStream *s);
int (*complete)(DnsStream *s, int error);
DnsTransaction *transaction;
DnsTransaction *transaction; /* when used by the transaction logic */
DnsQuery *query; /* when used by the DNS stub logic */
LIST_FIELDS(DnsStream, streams);
};
int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd);
DnsStream *dns_stream_free(DnsStream *s);
DnsStream *dns_stream_unref(DnsStream *s);
DnsStream *dns_stream_ref(DnsStream *s);
int dns_stream_write_packet(DnsStream *s, DnsPacket *p);
static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
assert(s);
if (s->fd < 0) /* already stopped? */
return false;
return !!s->write_packet;
}

View file

@ -0,0 +1,572 @@
/***
This file is part of systemd.
Copyright 2016 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 "fd-util.h"
#include "resolved-dns-stub.h"
#include "socket-util.h"
/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
* IP and UDP header sizes */
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
static int dns_stub_make_reply_packet(
uint16_t id,
int rcode,
DnsQuestion *q,
DnsAnswer *answer,
bool add_opt, /* add an OPT RR to this packet */
bool edns0_do, /* set the EDNS0 DNSSEC OK bit */
bool ad, /* set the DNSSEC authenticated data bit */
DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsResourceRecord *rr;
unsigned c = 0;
int r;
/* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
* roundtrips aren't expensive. */
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
if (r < 0)
return r;
/* If the client didn't do EDNS, clamp the rcode to 4 bit */
if (!add_opt && rcode > 0xF)
rcode = DNS_RCODE_SERVFAIL;
DNS_PACKET_HEADER(p)->id = id;
DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
1 /* qr */,
0 /* opcode */,
0 /* aa */,
0 /* tc */,
1 /* rd */,
1 /* ra */,
ad /* ad */,
0 /* cd */,
rcode));
r = dns_packet_append_question(p, q);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
DNS_ANSWER_FOREACH(rr, answer) {
r = dns_question_matches_rr(q, rr, NULL);
if (r < 0)
return r;
if (r > 0)
goto add;
r = dns_question_matches_cname_or_dname(q, rr, NULL);
if (r < 0)
return r;
if (r > 0)
goto add;
continue;
add:
r = dns_packet_append_rr(p, rr, NULL, NULL);
if (r < 0)
return r;
c++;
}
DNS_PACKET_HEADER(p)->ancount = htobe16(c);
if (add_opt) {
r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
if (r < 0)
return r;
}
*ret = p;
p = NULL;
return 0;
}
static void dns_stub_detach_stream(DnsStream *s) {
assert(s);
s->complete = NULL;
s->on_packet = NULL;
s->query = NULL;
}
static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
int r;
assert(m);
assert(p);
assert(reply);
if (s)
r = dns_stream_write_packet(s, reply);
else {
int fd;
/* Truncate the message to the right size */
if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) {
dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX);
DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC);
}
fd = manager_dns_stub_udp_fd(m);
if (fd < 0)
return log_debug_errno(fd, "Failed to get reply socket: %m");
/* Note that it is essential here that we explicitly choose the source IP address for this packet. This
* is because otherwise the kernel will choose it automatically based on the routing table and will
* thus pick 127.0.0.1 rather than 127.0.0.53. */
r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
}
if (r < 0)
return log_debug_errno(r, "Failed to send reply packet: %m");
return 0;
}
static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
int r;
assert(m);
assert(p);
r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply);
if (r < 0)
return log_debug_errno(r, "Failed to build failure packet: %m");
return dns_stub_send(m, s, p, reply);
}
static void dns_stub_query_complete(DnsQuery *q) {
int r;
assert(q);
assert(q->request_dns_packet);
switch (q->state) {
case DNS_TRANSACTION_SUCCESS: {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
r = dns_stub_make_reply_packet(
DNS_PACKET_ID(q->request_dns_packet),
q->answer_rcode,
q->question_idna,
q->answer,
!!q->request_dns_packet->opt,
DNS_PACKET_DO(q->request_dns_packet),
DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated,
&reply);
if (r < 0) {
log_debug_errno(r, "Failed to build reply packet: %m");
break;
}
(void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply);
break;
}
case DNS_TRANSACTION_RCODE_FAILURE:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode);
break;
case DNS_TRANSACTION_NOT_FOUND:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN);
break;
case DNS_TRANSACTION_TIMEOUT:
case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
/* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
break;
case DNS_TRANSACTION_NO_SERVERS:
case DNS_TRANSACTION_INVALID_REPLY:
case DNS_TRANSACTION_ERRNO:
case DNS_TRANSACTION_ABORTED:
case DNS_TRANSACTION_DNSSEC_FAILED:
case DNS_TRANSACTION_NO_TRUST_ANCHOR:
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
(void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL);
break;
case DNS_TRANSACTION_NULL:
case DNS_TRANSACTION_PENDING:
case DNS_TRANSACTION_VALIDATING:
default:
assert_not_reached("Impossible state");
}
/* If there's a packet to write set, let's leave the stream around */
if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) {
/* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The
* default completion action of the stream will drop the reference. */
dns_stub_detach_stream(q->request_dns_stream);
q->request_dns_stream = NULL;
}
dns_query_free(q);
}
static int dns_stub_stream_complete(DnsStream *s, int error) {
assert(s);
log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m");
assert(s->query);
dns_query_free(s->query);
return 0;
}
static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
DnsQuery *q = NULL;
int r;
assert(m);
assert(p);
assert(p->protocol == DNS_PROTOCOL_DNS);
/* Takes ownership of the *s stream object */
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
log_error("Got packet on unexpected IP range, refusing.");
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
goto fail;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR);
goto fail;
}
if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
log_debug("Got EDNS OPT field with unsupported version number.");
dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS);
goto fail;
}
if (dns_type_is_obsolete(p->question->keys[0]->type)) {
log_debug("Got message with obsolete key type, refusing.");
dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
goto fail;
}
if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
log_debug("Got request for zone transfer, refusing.");
dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
goto fail;
}
if (!DNS_PACKET_RD(p)) {
/* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
log_debug("Got request with recursion disabled, refusing.");
dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED);
goto fail;
}
if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
log_debug("Got request with DNSSEC CD bit set, refusing.");
dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP);
goto fail;
}
r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME);
if (r < 0) {
log_error_errno(r, "Failed to generate query object: %m");
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
goto fail;
}
/* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
q->clamp_ttl = true;
q->request_dns_packet = dns_packet_ref(p);
q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
q->complete = dns_stub_query_complete;
if (s) {
s->on_packet = NULL;
s->complete = dns_stub_stream_complete;
s->query = q;
}
r = dns_query_go(q);
if (r < 0) {
log_error_errno(r, "Failed to start query: %m");
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL);
goto fail;
}
log_info("Processing query...");
return;
fail:
if (s && DNS_STREAM_QUEUED(s))
dns_stub_detach_stream(s);
dns_query_free(q);
}
static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
Manager *m = userdata;
int r;
r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
if (r <= 0)
return r;
if (dns_packet_validate_query(p) > 0) {
log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
dns_stub_process_query(m, NULL, p);
} else
log_debug("Invalid DNS stub UDP packet, ignoring.");
return 0;
}
int manager_dns_stub_udp_fd(Manager *m) {
static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
};
int r;
if (m->dns_stub_udp_fd >= 0)
return m->dns_stub_udp_fd;
m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->dns_stub_udp_fd < 0)
return -errno;
r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
/* Make sure no traffic from outside the local host can leak to onto this socket */
r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in));
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
return m->dns_stub_udp_fd;
fail:
m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
return r;
}
static int on_dns_stub_stream_packet(DnsStream *s) {
assert(s);
assert(s->read_packet);
if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
dns_stub_process_query(s->manager, s, s->read_packet);
} else
log_debug("Invalid DNS stub TCP packet, ignoring.");
/* Drop the reference to the stream. Either a query was created and added its own reference to the stream now,
* or that didn't happen in which case we want to free the stream */
dns_stream_unref(s);
return 0;
}
static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
DnsStream *stream;
Manager *m = userdata;
int cfd, r;
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return -errno;
}
r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd);
if (r < 0) {
safe_close(cfd);
return r;
}
stream->on_packet = on_dns_stub_stream_packet;
/* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action
* of the stream, or by our packet callback, or when the manager is shut down. */
return 0;
}
int manager_dns_stub_tcp_fd(Manager *m) {
static const int one = 1;
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
.in.sin_port = htobe16(53),
};
int r;
if (m->dns_stub_tcp_fd >= 0)
return m->dns_stub_tcp_fd;
m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->dns_stub_tcp_fd < 0)
return -errno;
r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
/* Make sure no traffic from outside the local host can leak to onto this socket */
r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3);
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in));
if (r < 0) {
r = -errno;
goto fail;
}
r = listen(m->dns_stub_tcp_fd, SOMAXCONN);
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
return m->dns_stub_tcp_fd;
fail:
m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
return r;
}
int manager_dns_stub_start(Manager *m) {
int r;
assert(m);
r = manager_dns_stub_udp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
r = manager_dns_stub_tcp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
return 0;
eaddrinuse:
log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support.");
manager_dns_stub_stop(m);
return 0;
}
void manager_dns_stub_stop(Manager *m) {
assert(m);
m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
}

View file

@ -0,0 +1,31 @@
#pragma once
/***
This file is part of systemd.
Copyright 2016 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 "resolved-manager.h"
/* 127.0.0.53 in native endian */
#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
int manager_dns_stub_udp_fd(Manager *m);
int manager_dns_stub_tcp_fd(Manager *m);
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);

View file

@ -60,7 +60,14 @@ static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
static void dns_transaction_close_connection(DnsTransaction *t) {
assert(t);
t->stream = dns_stream_free(t->stream);
if (t->stream) {
/* Let's detach the stream from our transaction, in case something else keeps a reference to it. */
t->stream->complete = NULL;
t->stream->on_packet = NULL;
t->stream->transaction = NULL;
t->stream = dns_stream_unref(t->stream);
}
t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
t->dns_udp_fd = safe_close(t->dns_udp_fd);
}
@ -444,7 +451,7 @@ static int on_stream_complete(DnsStream *s, int error) {
t = s->transaction;
p = dns_packet_ref(s->read_packet);
t->stream = dns_stream_free(t->stream);
dns_transaction_close_connection(t);
if (ERRNO_IS_DISCONNECT(error)) {
usec_t usec;
@ -556,7 +563,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
r = dns_stream_write_packet(t->stream, t->sent);
if (r < 0) {
t->stream = dns_stream_free(t->stream);
t->stream = dns_stream_unref(t->stream);
return r;
}
@ -1274,7 +1281,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
/* Let's then prune all outdated entries */
dns_cache_prune(&t->scope->cache);
r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
r = dns_cache_lookup(&t->scope->cache, t->key, t->clamp_ttl, &t->answer_rcode, &t->answer, &t->answer_authenticated);
if (r < 0)
return r;
if (r > 0) {

View file

@ -74,6 +74,8 @@ struct DnsTransaction {
bool initial_jitter_scheduled:1;
bool initial_jitter_elapsed:1;
bool clamp_ttl:1;
DnsPacket *sent, *received;
DnsAnswer *answer;

View file

@ -28,7 +28,23 @@
#include "strv.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_dnssec_mode, dnssec_mode, DnssecMode);
static int property_get_dnssec_mode(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Link *l = userdata;
assert(reply);
assert(l);
return sd_bus_message_append(reply, "s", dnssec_mode_to_string(link_get_dnssec_mode(l)));
}
static int property_get_dns(
sd_bus *bus,
@ -214,6 +230,9 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
if (sz != FAMILY_ADDRESS_SIZE(family))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
if (!dns_server_address_valid(family, d))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
@ -249,6 +268,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
dns_server_unlink_marked(l->dns_servers);
link_allocate_scopes(l);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);
@ -330,6 +350,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
dns_search_domain_unlink_marked(l->search_domains);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);
@ -368,6 +389,8 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -400,6 +423,8 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -430,6 +455,8 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
link_set_dnssec_mode(l, mode);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -473,6 +500,8 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -491,6 +520,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);
@ -504,7 +534,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0),
SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0),
SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0),
SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),

View file

@ -22,7 +22,10 @@
#include "sd-network.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "resolved-link.h"
#include "string-util.h"
@ -49,6 +52,9 @@ int link_new(Manager *m, Link **ret, int ifindex) {
l->dnssec_mode = _DNSSEC_MODE_INVALID;
l->operstate = IF_OPER_UNKNOWN;
if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
return -ENOMEM;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
return r;
@ -93,6 +99,8 @@ Link *link_free(Link *l) {
dns_scope_free(l->mdns_ipv4_scope);
dns_scope_free(l->mdns_ipv6_scope);
free(l->state_file);
free(l);
return NULL;
}
@ -165,7 +173,7 @@ void link_add_rrs(Link *l, bool force_remove) {
link_address_add_rrs(a, force_remove);
}
int link_update_rtnl(Link *l, sd_netlink_message *m) {
int link_process_rtnl(Link *l, sd_netlink_message *m) {
const char *n = NULL;
int r;
@ -190,6 +198,27 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
return 0;
}
static int link_update_dns_server_one(Link *l, const char *name) {
union in_addr_union a;
DnsServer *s;
int family, r;
assert(l);
assert(name);
r = in_addr_from_string_auto(name, &family, &a);
if (r < 0)
return r;
s = dns_server_find(l->dns_servers, family, &a, 0);
if (s) {
dns_server_move_back_and_unmark(s);
return 0;
}
return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0);
}
static int link_update_dns_servers(Link *l) {
_cleanup_strv_free_ char **nameservers = NULL;
char **nameserver;
@ -208,22 +237,9 @@ static int link_update_dns_servers(Link *l) {
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);
r = link_update_dns_server_one(l, *nameserver);
if (r < 0)
goto clear;
s = dns_server_find(l->dns_servers, family, &a, 0);
if (s)
dns_server_move_back_and_unmark(s);
else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0);
if (r < 0)
goto clear;
}
}
dns_server_unlink_marked(l->dns_servers);
@ -341,7 +357,6 @@ clear:
static int link_update_dnssec_negative_trust_anchors(Link *l) {
_cleanup_strv_free_ char **ntas = NULL;
_cleanup_set_free_free_ Set *ns = NULL;
char **i;
int r;
assert(l);
@ -358,11 +373,9 @@ static int link_update_dnssec_negative_trust_anchors(Link *l) {
if (!ns)
return -ENOMEM;
STRV_FOREACH(i, ntas) {
r = set_put_strdup(ns, *i);
if (r < 0)
return r;
}
r = set_put_strdupv(ns, ntas);
if (r < 0)
return r;
set_free_free(l->dnssec_negative_trust_anchors);
l->dnssec_negative_trust_anchors = ns;
@ -379,6 +392,9 @@ static int link_update_search_domain_one(Link *l, const char *name, bool route_o
DnsSearchDomain *d;
int r;
assert(l);
assert(name);
r = dns_search_domain_find(l->search_domains, name, &d);
if (r < 0)
return r;
@ -439,7 +455,7 @@ clear:
return r;
}
static int link_is_unmanaged(Link *l) {
static int link_is_managed(Link *l) {
_cleanup_free_ char *state = NULL;
int r;
@ -447,11 +463,11 @@ static int link_is_unmanaged(Link *l) {
r = sd_network_link_get_setup_state(l->ifindex, &state);
if (r == -ENODATA)
return 1;
return 0;
if (r < 0)
return r;
return STR_IN_SET(state, "pending", "unmanaged");
return !STR_IN_SET(state, "pending", "unmanaged");
}
static void link_read_settings(Link *l) {
@ -461,12 +477,12 @@ static void link_read_settings(Link *l) {
/* Read settings from networkd, except when networkd is not managing this interface. */
r = link_is_unmanaged(l);
r = link_is_managed(l);
if (r < 0) {
log_warning_errno(r, "Failed to determine whether interface %s is managed: %m", l->name);
return;
}
if (r > 0) {
if (r == 0) {
/* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */
if (l->is_managed)
@ -503,10 +519,11 @@ static void link_read_settings(Link *l) {
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
}
int link_update_monitor(Link *l) {
int link_update(Link *l) {
assert(l);
link_read_settings(l);
link_load_user(l);
link_allocate_scopes(l);
link_add_rrs(l, false);
@ -838,3 +855,261 @@ bool link_address_relevant(LinkAddress *a, bool local_multicast) {
return true;
}
static bool link_needs_save(Link *l) {
assert(l);
/* Returns true if any of the settings where set different from the default */
if (l->is_managed)
return false;
if (l->llmnr_support != RESOLVE_SUPPORT_YES ||
l->mdns_support != RESOLVE_SUPPORT_NO ||
l->dnssec_mode != _DNSSEC_MODE_INVALID)
return true;
if (l->dns_servers ||
l->search_domains)
return true;
if (!set_isempty(l->dnssec_negative_trust_anchors))
return true;
return false;
}
int link_save_user(Link *l) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *v;
int r;
assert(l);
assert(l->state_file);
if (!link_needs_save(l)) {
(void) unlink(l->state_file);
return 0;
}
r = mkdir_parents(l->state_file, 0700);
if (r < 0)
goto fail;
r = fopen_temporary(l->state_file, &f, &temp_path);
if (r < 0)
goto fail;
fputs("# This is private data. Do not parse.\n", f);
v = resolve_support_to_string(l->llmnr_support);
if (v)
fprintf(f, "LLMNR=%s\n", v);
v = resolve_support_to_string(l->mdns_support);
if (v)
fprintf(f, "MDNS=%s\n", v);
v = dnssec_mode_to_string(l->dnssec_mode);
if (v)
fprintf(f, "DNSSEC=%s\n", v);
if (l->dns_servers) {
DnsServer *server;
fputs("SERVERS=", f);
LIST_FOREACH(servers, server, l->dns_servers) {
if (server != l->dns_servers)
fputc(' ', f);
v = dns_server_string(server);
if (!v) {
r = -ENOMEM;
goto fail;
}
fputs(v, f);
}
fputc('\n', f);
}
if (l->search_domains) {
DnsSearchDomain *domain;
fputs("DOMAINS=", f);
LIST_FOREACH(domains, domain, l->search_domains) {
if (domain != l->search_domains)
fputc(' ', f);
if (domain->route_only)
fputc('~', f);
fputs(DNS_SEARCH_DOMAIN_NAME(domain), f);
}
fputc('\n', f);
}
if (!set_isempty(l->dnssec_negative_trust_anchors)) {
bool space = false;
Iterator i;
char *nta;
fputs("NTAS=", f);
SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) {
if (space)
fputc(' ', f);
fputs(nta, f);
space = true;
}
fputc('\n', f);
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
if (rename(temp_path, l->state_file) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
(void) unlink(l->state_file);
if (temp_path)
(void) unlink(temp_path);
return log_error_errno(r, "Failed to save link data %s: %m", l->state_file);
}
int link_load_user(Link *l) {
_cleanup_free_ char
*llmnr = NULL,
*mdns = NULL,
*dnssec = NULL,
*servers = NULL,
*domains = NULL,
*ntas = NULL;
ResolveSupport s;
int r;
assert(l);
assert(l->state_file);
/* Try to load only a single time */
if (l->loaded)
return 0;
l->loaded = true;
if (l->is_managed)
return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */
r = parse_env_file(l->state_file, NEWLINE,
"LLMNR", &llmnr,
"MDNS", &mdns,
"DNSSEC", &dnssec,
"SERVERS", &servers,
"DOMAINS", &domains,
"NTAS", &ntas,
NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
goto fail;
link_flush_settings(l);
/* If we can't recognize the LLMNR or MDNS setting we don't override the default */
s = resolve_support_from_string(llmnr);
if (s >= 0)
l->llmnr_support = s;
s = resolve_support_from_string(mdns);
if (s >= 0)
l->mdns_support = s;
/* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
l->dnssec_mode = dnssec_mode_from_string(dnssec);
if (servers) {
const char *p = servers;
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
goto fail;
if (r == 0)
break;
r = link_update_dns_server_one(l, word);
if (r < 0) {
log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
continue;
}
}
}
if (domains) {
const char *p = domains;
for (;;) {
_cleanup_free_ char *word = NULL;
const char *n;
bool is_route;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
goto fail;
if (r == 0)
break;
is_route = word[0] == '~';
n = is_route ? word + 1 : word;
r = link_update_search_domain_one(l, n, is_route);
if (r < 0) {
log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
continue;
}
}
}
if (ntas) {
_cleanup_set_free_free_ Set *ns = NULL;
ns = set_new(&dns_name_hash_ops);
if (!ns) {
r = -ENOMEM;
goto fail;
}
r = set_put_strsplit(ns, ntas, NULL, 0);
if (r < 0)
goto fail;
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
}
return 0;
fail:
return log_error_errno(r, "Failed to load link data %s: %m", l->state_file);
}
void link_remove_user(Link *l) {
assert(l);
assert(l->state_file);
(void) unlink(l->state_file);
}

View file

@ -81,12 +81,15 @@ struct Link {
char name[IF_NAMESIZE];
uint32_t mtu;
uint8_t operstate;
bool loaded;
char *state_file;
};
int link_new(Manager *m, Link **ret, int ifindex);
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
int link_process_rtnl(Link *l, sd_netlink_message *m);
int link_update(Link *l);
bool link_relevant(Link *l, int family, bool local_multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
@ -102,6 +105,10 @@ void link_next_dns_server(Link *l);
DnssecMode link_get_dnssec_mode(Link *l);
bool link_dnssec_supported(Link *l);
int link_save_user(Link *l);
int link_load_user(Link *l);
void link_remove_user(Link *l);
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);

View file

@ -91,18 +91,19 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
DnsScope *scope;
int r;
assert(s);
assert(fd >= 0);
assert(m);
r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
if (r <= 0)
return r;
scope = manager_find_scope(m, p);
if (!scope) {
if (!scope)
log_warning("Got LLMNR UDP packet on unknown scope. Ignoring.");
return 0;
}
if (dns_packet_validate_reply(p) > 0) {
log_debug("Got LLMNR reply packet for id %u", DNS_PACKET_ID(p));
else if (dns_packet_validate_reply(p) > 0) {
log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p));
dns_scope_check_conflicts(scope, p);
@ -111,7 +112,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
dns_transaction_process_reply(t, p);
} else if (dns_packet_validate_query(p) > 0) {
log_debug("Got LLMNR query packet for id %u", DNS_PACKET_ID(p));
log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p));
dns_scope_process_query(scope, NULL, p);
} else
@ -283,25 +284,19 @@ static int on_llmnr_stream_packet(DnsStream *s) {
DnsScope *scope;
assert(s);
assert(s->read_packet);
scope = manager_find_scope(s->manager, s->read_packet);
if (!scope) {
if (!scope)
log_warning("Got LLMNR TCP packet on unknown scope. Ignoring.");
return 0;
}
if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet));
else if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
dns_scope_process_query(scope, s, s->read_packet);
/* If no reply packet was set, we free the stream */
if (s->write_packet)
return 0;
} else
log_debug("Invalid LLMNR TCP packet.");
log_debug("Invalid LLMNR TCP packet, ignoring.");
dns_stream_free(s);
dns_stream_unref(s);
return 0;
}

View file

@ -23,6 +23,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dirent-util.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "fileio-label.h"
@ -35,6 +36,7 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
#include "resolved-dns-stub.h"
#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
@ -78,11 +80,11 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
goto fail;
}
r = link_update_rtnl(l, mm);
r = link_process_rtnl(l, mm);
if (r < 0)
goto fail;
r = link_update_monitor(l);
r = link_update(l);
if (r < 0)
goto fail;
@ -95,6 +97,7 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
case RTM_DELLINK:
if (l) {
log_debug("Removing link %i/%s", l->ifindex, l->name);
link_remove_user(l);
link_free(l);
}
@ -279,7 +282,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
sd_network_monitor_flush(m->network_monitor);
HASHMAP_FOREACH(l, m->links, i) {
r = link_update_monitor(l);
r = link_update(l);
if (r < 0)
log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex);
}
@ -491,6 +494,7 @@ int manager_new(Manager **ret) {
m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
m->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1;
m->hostname_fd = -1;
m->llmnr_support = RESOLVE_SUPPORT_YES;
@ -540,6 +544,8 @@ int manager_new(Manager **ret) {
(void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
(void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m);
manager_cleanup_saved_user(m);
*ret = m;
m = NULL;
@ -551,6 +557,10 @@ int manager_start(Manager *m) {
assert(m);
r = manager_dns_stub_start(m);
if (r < 0)
return r;
r = manager_llmnr_start(m);
if (r < 0)
return r;
@ -580,6 +590,11 @@ Manager *manager_free(Manager *m) {
dns_scope_free(m->unicast_scope);
/* At this point only orphaned streams should remain. All others should have been freed already by their
* owners */
while (m->dns_streams)
dns_stream_unref(m->dns_streams);
hashmap_free(m->links);
hashmap_free(m->dns_transactions);
@ -591,6 +606,7 @@ Manager *manager_free(Manager *m) {
manager_llmnr_stop(m);
manager_mdns_stop(m);
manager_dns_stub_stop(m);
sd_bus_slot_unref(m->prepare_for_sleep_slot);
sd_event_source_unref(m->bus_retry_event_source);
@ -805,7 +821,14 @@ int manager_write(Manager *m, int fd, DnsPacket *p) {
return 0;
}
static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
static int manager_ipv4_send(
Manager *m,
int fd,
int ifindex,
const struct in_addr *destination,
uint16_t port,
const struct in_addr *source,
DnsPacket *p) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
};
@ -818,14 +841,14 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_ad
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in.sin_addr = *addr;
sa.in.sin_addr = *destination;
sa.in.sin_port = htobe16(port),
mh.msg_iov = &iov;
@ -849,12 +872,23 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_ad
pi = (struct in_pktinfo*) CMSG_DATA(cmsg);
pi->ipi_ifindex = ifindex;
if (source)
pi->ipi_spec_dst = *source;
}
return sendmsg_loop(fd, &mh, 0);
}
static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) {
static int manager_ipv6_send(
Manager *m,
int fd,
int ifindex,
const struct in6_addr *destination,
uint16_t port,
const struct in6_addr *source,
DnsPacket *p) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
};
@ -867,14 +901,14 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_a
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in6.sin6_addr = *addr;
sa.in6.sin6_addr = *destination;
sa.in6.sin6_port = htobe16(port),
sa.in6.sin6_scope_id = ifindex;
@ -899,24 +933,36 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_a
pi = (struct in6_pktinfo*) CMSG_DATA(cmsg);
pi->ipi6_ifindex = ifindex;
if (source)
pi->ipi6_addr = *source;
}
return sendmsg_loop(fd, &mh, 0);
}
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) {
int manager_send(
Manager *m,
int fd,
int ifindex,
int family,
const union in_addr_union *destination,
uint16_t port,
const union in_addr_union *source,
DnsPacket *p) {
assert(m);
assert(fd >= 0);
assert(addr);
assert(destination);
assert(port > 0);
assert(p);
log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
return manager_ipv4_send(m, fd, ifindex, &destination->in, port, &source->in, p);
if (family == AF_INET6)
return manager_ipv6_send(m, fd, ifindex, &addr->in6, port, p);
return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p);
return -EAFNOSUPPORT;
}
@ -1153,7 +1199,7 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) {
return 0;
}
int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) {
DnsSearchDomain *d;
Iterator i;
Link *l;
@ -1167,6 +1213,11 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
return r;
LIST_FOREACH(domains, d, m->search_domains) {
if (filter_route >= 0 &&
d->route_only != !!filter_route)
continue;
r = ordered_set_put(*domains, d->name);
if (r == -EEXIST)
continue;
@ -1177,6 +1228,11 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(domains, d, l->search_domains) {
if (filter_route >= 0 &&
d->route_only != !!filter_route)
continue;
r = ordered_set_put(*domains, d->name);
if (r == -EEXIST)
continue;
@ -1259,3 +1315,58 @@ void manager_flush_caches(Manager *m) {
log_info("Flushed all caches.");
}
void manager_cleanup_saved_user(Manager *m) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r;
assert(m);
/* Clean up all saved per-link files in /run/systemd/resolve/netif/ that don't have a matching interface
* anymore. These files are created to persist settings pushed in by the user via the bus, so that resolved can
* be restarted without losing this data. */
d = opendir("/run/systemd/resolve/netif/");
if (!d) {
if (errno == ENOENT)
return;
log_warning_errno(errno, "Failed to open interface directory: %m");
return;
}
FOREACH_DIRENT_ALL(de, d, log_error_errno(errno, "Failed to read interface directory: %m")) {
_cleanup_free_ char *p = NULL;
int ifindex;
Link *l;
if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG))
continue;
if (STR_IN_SET(de->d_name, ".", ".."))
continue;
r = parse_ifindex(de->d_name, &ifindex);
if (r < 0) /* Probably some temporary file from a previous run. Delete it */
goto rm;
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l) /* link vanished */
goto rm;
if (l->is_managed) /* now managed by networkd, hence the bus settings are useless */
goto rm;
continue;
rm:
p = strappend("/run/systemd/resolve/netif/", de->d_name);
if (!p) {
log_oom();
return;
}
(void) unlink(p);
}
}

View file

@ -72,7 +72,6 @@ struct Manager {
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
bool permit_domain_search;
bool need_builtin_fallbacks:1;
@ -129,6 +128,13 @@ struct Manager {
Set* etc_hosts_by_address;
Hashmap* etc_hosts_by_name;
usec_t etc_hosts_last, etc_hosts_mtime;
/* Local DNS stub on 127.0.0.53:53 */
int dns_stub_udp_fd;
int dns_stub_tcp_fd;
sd_event_source *dns_stub_udp_event_source;
sd_event_source *dns_stub_tcp_event_source;
};
/* Manager */
@ -141,7 +147,7 @@ int manager_start(Manager *m);
uint32_t manager_find_mtu(Manager *m);
int manager_write(Manager *m, int fd, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret);
int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr);
@ -162,7 +168,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
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);
int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route);
DnssecMode manager_get_dnssec_mode(Manager *m);
bool manager_dnssec_supported(Manager *m);
@ -172,3 +178,5 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
bool manager_routable(Manager *m, int family);
void manager_flush_caches(Manager *m);
void manager_cleanup_saved_user(Manager *m);

View file

@ -194,10 +194,13 @@ static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *doma
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);
"# This is a dynamic resolv.conf file for connecting local clients directly to\n"
"# all known DNS servers.\n#\n"
"# Third party programs must not access this file directly, but only through the\n"
"# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n"
"# replace this symlink by a static file or a different symlink.\n#\n"
"# See systemd-resolved.service(8) for details about the supported modes of\n"
"# operation for /etc/resolv.conf.\n\n", f);
if (ordered_set_isempty(dns))
fputs("# No DNS servers known.\n", f);
@ -232,7 +235,7 @@ int manager_write_resolv_conf(Manager *m) {
if (r < 0)
return log_warning_errno(r, "Failed to compile list of DNS servers: %m");
r = manager_compile_search_domains(m, &domains);
r = manager_compile_search_domains(m, &domains, false);
if (r < 0)
return log_warning_errno(r, "Failed to compile list of search domains: %m");

View file

@ -67,7 +67,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
r = drop_privileges(uid, gid, 0);
/* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */
r = drop_privileges(uid, gid,
(UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */
(UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */
(UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */);
if (r < 0)
goto finish;
@ -88,6 +92,13 @@ int main(int argc, char *argv[]) {
/* Write finish default resolv.conf to avoid a dangling symlink */
(void) manager_write_resolv_conf(m);
/* Let's drop the remaining caps now */
r = capability_bounding_set_drop(0, true);
if (r < 0) {
log_error_errno(r, "Failed to drop remaining caps: %m");
goto finish;
}
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");

View file

@ -33,6 +33,19 @@
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1)
static void verify_rr_copy(DnsResourceRecord *rr) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
const char *a, *b;
assert_se(copy = dns_resource_record_copy(rr));
assert_se(dns_resource_record_equal(copy, rr) > 0);
assert_se(a = dns_resource_record_to_string(rr));
assert_se(b = dns_resource_record_to_string(copy));
assert_se(streq(a, b));
}
static uint64_t hash(DnsResourceRecord *rr) {
struct siphash state;
@ -66,6 +79,8 @@ static void test_packet_from_file(const char* filename, bool canonical) {
assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0);
verify_rr_copy(rr);
s = dns_resource_record_to_string(rr);
assert_se(s);
puts(s);
@ -78,6 +93,8 @@ static void test_packet_from_file(const char* filename, bool canonical) {
assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0);
assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0);
verify_rr_copy(rr);
s2 = dns_resource_record_to_string(rr);
assert_se(s2);
assert_se(streq(s, s2));

View file

@ -1048,7 +1048,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
case SD_BUS_TYPE_BOOLEAN: {
unsigned b;
bool *p = userdata;
int *p = userdata;
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)

View file

@ -57,11 +57,11 @@ typedef struct StatusInfo {
char *timezone;
usec_t rtc_time;
bool rtc_local;
int rtc_local;
bool ntp_enabled;
bool ntp_capable;
bool ntp_synced;
int ntp_enabled;
int ntp_capable;
int ntp_synced;
} StatusInfo;
static void status_info_clear(StatusInfo *info) {

View file

@ -2178,7 +2178,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
Iterator iterator;
unsigned v = 0;
Item *i;
int r;
int r = 0;
assert(fn);

View file

@ -14,7 +14,7 @@ m4_ifdef(`HAVE_SMACK_RUN_LABEL',
t /etc/mtab - - - - security.SMACK64=_
)m4_dnl
m4_ifdef(`ENABLE_RESOLVED',
L! /etc/resolv.conf - - - - ../run/systemd/resolve/resolv.conf
L! /etc/resolv.conf - - - - ../usr/lib/systemd/resolv.conf
)m4_dnl
C /etc/nsswitch.conf - - - -
m4_ifdef(`HAVE_PAM',

View file

@ -23,7 +23,7 @@ Type=notify
Restart=always
RestartSec=0
ExecStart=@rootlibexecdir@/systemd-resolved
CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER
CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_NET_RAW CAP_NET_BIND_SERVICE
ProtectSystem=full
ProtectHome=yes
WatchdogSec=3min