mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-03-11 04:58:19 +03:00
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:
commit
a2c28c6451
@ -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 += \
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)); )
|
||||
|
@ -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; \
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
11
src/resolve/resolv.conf
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
572
src/resolve/resolved-dns-stub.c
Normal file
572
src/resolve/resolved-dns-stub.c
Normal 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);
|
||||
}
|
31
src/resolve/resolved-dns-stub.h
Normal file
31
src/resolve/resolved-dns-stub.h
Normal 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);
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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...");
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user