<term><varname>ReadEtcHosts=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
<command>systemd-resolved</command> will read <filename>/etc/hosts</filename>, and try to resolve
- hosts or address by using the entries in the file before sending query to DNS servers.
+ hosts or addresses by using the entries in the file before sending query to DNS servers.
</para>
<xi:include href="version-info.xml" xpointer="v240"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ReadStaticRecords=</varname></term>
+ <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
+ <command>systemd-resolved</command> will read
+ <filename>/etc/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/run/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/usr/lib/systemd/resolve/static.d/*.rr</filename>, and try to resolve lookups by using the
+ entries in these files before sending query to DNS servers. This functionality is very similar to the
+ one controlled by <varname>ReadEtcHosts=</varname>, but allows more flexible control of DNS resource
+ records fields beyond just A/AAAA/PTR. See
+ <citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details.</para>
+
+ <para>If both this option and <varname>ReadEtcHosts=</varname> are enabled then this mechanism takes
+ precedence: any records discovered via static resource records will take precedence over records
+ under the same name from <filename>/etc/hosts</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>ResolveUnicastSingleLabel=</varname></term>
<listitem><para>Takes a boolean argument. When false (the default),
<member><citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
['systemd.pcrlock', '5', ['systemd.pcrlock.d'], ''],
['systemd.preset', '5', [], ''],
['systemd.resource-control', '5', [], ''],
+ ['systemd.rr', '5', [], 'ENABLE_RESOLVE'],
['systemd.scope', '5', [], ''],
['systemd.service', '5', [], ''],
['systemd.slice', '5', [], ''],
<listitem><para>The hostname <literal>_localdnsproxy</literal> is resolved to the IP address 127.0.0.54,
i.e. the address the local DNS proxy (see above) is listening on.</para></listitem>
+ <listitem><para>The files matching <filename>/etc/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/run/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename>,
+ <filename>/usr/lib/systemd/resolve/static.d/*.rr</filename> may be used to define arbitrary
+ records. See
+ <citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. Support for this may be disabled with <varname>ReadStaticRecords=no</varname>, see
+ <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+
<listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their
configured addresses and back, but they will not affect lookups for non-address types (like MX).
Support for <filename>/etc/hosts</filename> may be disabled with <varname>ReadEtcHosts=no</varname>,
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.dns-delegate</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.dnssd</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd.rr"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ conditional='ENABLE_RESOLVE'>
+
+ <refentryinfo>
+ <title>systemd.rr</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd.rr</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd.rr</refname>
+ <refpurpose>Local static DNS resource record definitions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><simplelist>
+ <member><filename>/etc/systemd/resolve/static.d/*.rr</filename></member>
+ <member><filename>/run/systemd/resolve/static.d/*.rr</filename></member>
+ <member><filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename></member>
+ <member><filename>/usr/lib/systemd/resolve/static.d/*.rr</filename></member>
+ </simplelist></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>*.rr</filename> files may be used to define resource record sets ("RRsets") that shall be
+ resolvable locally, similar in style to address records defined by <filename>/etc/hosts</filename> (see
+ <citerefentry><refentrytitle>hosts</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details). These files are read by
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ and are used to synthesize local responses to local queries matching the defined resource record set.</para>
+
+ <para>These drop-in files are in JSON format. Each file may either contain a single top-level DNS RR
+ object, or an array of one or more DNS RR objects. Each RR object has at least a <literal>key</literal>
+ subobject consisting of a <literal>name</literal> string field and a <literal>type</literal> integer
+ field (which contains the RR type in numeric form). Depending on the chosen type the RR object also has
+ the following fields:</para>
+
+ <itemizedlist>
+ <listitem><para>For A/AAAA RRs, the RR object should have an <literal>address</literal> field set to
+ either an IP address formatted as string, or an array consisting of 4 or 16 8-bit unsigned integers for
+ the IP address.</para></listitem>
+
+ <listitem><para>For PTR/NS/CNAME/DNAME RRs, the RR object should have a <literal>name</literal> field
+ set to the name the record shall point to.</para></listitem>
+ </itemizedlist>
+
+ <para>This JSON serialization of DNS RRs matches the one returned by <command>resolvectl</command>.</para>
+
+ <para>Currently no other RR types are supported.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+ <example>
+ <title>Simple A Record</title>
+ <para>To make local address lookups for <literal>foobar.example.com</literal> resolve to the
+ 192.168.100.1 IPv4 address, create
+ <filename>/run/systemd/resolve/static.d/foobar_example_com.rr</filename>:
+
+ <programlisting>
+{
+ "key" : {
+ "type" : 1,
+ "name" : "foobar.example.com"
+ },
+ "address" : [ 192, 168, 100, 1 ]
+}</programlisting></para>
+
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>hosts</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+
+</refentry>
'resolved-mdns.c',
'resolved-resolv-conf.c',
'resolved-socket-graveyard.c',
+ 'resolved-static-records.c',
'resolved-util.c',
'resolved-varlink.c',
)
#include "resolved-etc-hosts.h"
#include "resolved-hook.h"
#include "resolved-manager.h"
+#include "resolved-static-records.h"
#include "resolved-timeouts.h"
#include "set.h"
#include "string-util.h"
return 1;
}
+static int dns_query_try_static_records(DnsQuery *q) {
+ int r;
+
+ assert(q);
+
+ if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
+ return 0;
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ r = manager_static_records_lookup(
+ q->manager,
+ q->question_bypass ? q->question_bypass->question : q->question_utf8,
+ &answer);
+ if (r <= 0)
+ return r;
+
+ dns_query_reset_answer(q);
+
+ q->answer = TAKE_PTR(answer);
+ q->answer_rcode = DNS_RCODE_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
+
+ return 1;
+}
+
static int dns_query_go_scopes(DnsQuery *q) {
int r;
q->state != DNS_TRANSACTION_NULL)
return 0;
+ r = dns_query_try_static_records(q);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
+ return 1;
+ }
+
r = dns_query_try_etc_hosts(q);
if (r < 0)
return r;
Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)
+Resolve.ReadStaticRecords, config_parse_bool, 0, offsetof(Manager, read_static_records)
Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
Resolve.CacheFromLocalhost, config_parse_bool, 0, offsetof(Manager, cache_from_localhost)
#include "resolved-mdns.h"
#include "resolved-resolv-conf.h"
#include "resolved-socket-graveyard.h"
+#include "resolved-static-records.h"
#include "resolved-util.h"
#include "resolved-varlink.h"
#include "set.h"
m->enable_cache = DNS_CACHE_MODE_YES;
m->dns_stub_listener_mode = DNS_STUB_LISTENER_YES;
m->read_etc_hosts = true;
+ m->read_static_records = true;
m->resolve_unicast_single_label = false;
m->cache_from_localhost = false;
m->stale_retention_usec = 0;
m->delegates = hashmap_free(m->delegates);
dns_trust_anchor_flush(&m->trust_anchor);
manager_etc_hosts_flush(m);
+ manager_static_records_flush(m);
manager_set_defaults(m);
.read_resolv_conf = true,
.need_builtin_fallbacks = true,
.etc_hosts_last = USEC_INFINITY,
+ .static_records_last = USEC_INFINITY,
.sigrtmin18_info.memory_pressure_handler = manager_memory_pressure,
.sigrtmin18_info.memory_pressure_userdata = m,
dns_trust_anchor_flush(&m->trust_anchor);
manager_etc_hosts_flush(m);
+ manager_static_records_flush(m);
while ((sb = hashmap_first(m->dns_service_browsers)))
dns_service_browser_free(sb);
struct stat etc_hosts_stat;
bool read_etc_hosts;
+ /* Data from {/etc,/run,/usr/local/lib,/usr/lib}/systemd/resolve/static.d/ */
+ Hashmap *static_records;
+ usec_t static_records_last;
+ Set *static_records_stat;
+ bool read_static_records;
+
/* List of refused DNS Record Types */
Set *refuse_record_types;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "constants.h"
+#include "dns-answer.h"
+#include "dns-domain.h"
+#include "dns-question.h"
+#include "dns-rr.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "json-util.h"
+#include "log.h"
+#include "resolved-manager.h"
+#include "resolved-static-records.h"
+#include "set.h"
+#include "stat-util.h"
+
+/* This implements a mechanism to extend what systemd-resolved resolves locally, via .rr drop-ins in
+ * {/etc,/run,/usr/local/lib,/usr/lib}/systemd/resolve/static.d/. These files are in JSON format, and are RR
+ * serializations, that match the usual way we serialize RRs to JSON.
+ *
+ * Note that this deliberately doesn't use the (probably more user-friendly) classic DNS zone file format,
+ * to keep things a bit simpler, and symmetric to the places we currently already generate JSON
+ * serializations of DNS RRs. Also note the semantics are different from DNS zone file format, for example
+ * regarding delegation (i.e. the RRs defined here have no effect on subdomains), which is probably nicer for
+ * one-off mappings of domains to specific resources. Or in other words, this is supposed to be a drop-in
+ * based alternative to /etc/hosts, not a one to DNS zone files. (The JSON format is also a lot more
+ * extensible to us, for example we could teach it to map certain lookups to specific DNS errors, or extend
+ * it so that subdomains always get NXDOMAIN or similar).
+ *
+ * (That said, if there's a good reason, we can also support *.zone files too one day).
+ */
+
+/* Recheck static records at most once every 2s */
+#define STATIC_RECORDS_RECHECK_USEC (2*USEC_PER_SEC)
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ answer_by_name_hash_ops,
+ char,
+ dns_name_hash_func,
+ dns_name_compare_func,
+ DnsAnswer,
+ dns_answer_unref);
+
+static int load_static_record_file_item(sd_json_variant *rj, Hashmap **records) {
+ int r;
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ r = dns_resource_record_from_json(rj, &rr);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS record from JSON: %m");
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *a =
+ hashmap_remove(*records, dns_resource_key_name(rr->key));
+
+ r = dns_answer_add_extend_full(&a, rr, /* ifindex= */ 0, DNS_ANSWER_AUTHENTICATED, /* rrsig= */ NULL, /* until= */ USEC_INFINITY);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append RR to DNS answer: %m");
+
+ DnsAnswerItem *item = ASSERT_PTR(ordered_set_first(a->items));
+
+ r = hashmap_ensure_put(records, &answer_by_name_hash_ops, dns_resource_key_name(item->rr->key), a);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add RR to static record set: %m");
+
+ TAKE_PTR(a);
+
+ log_debug("Added static resource record: %s", dns_resource_record_to_string(rr));
+ return 1;
+}
+
+static int load_static_record_file(const ConfFile *cf, Hashmap **records, Set **stats) {
+ int r;
+
+ assert(cf);
+ assert(records);
+ assert(stats);
+
+ /* Have we seen this file before? Then we might as well skip loading it again, it wouldn't have any
+ * additional effect anyway. (Note: masking/overriding has already been applied before we reach this
+ * point, here everything is purely additive.) */
+ if (set_contains(*stats, &cf->st))
+ return 0;
+
+ _cleanup_free_ struct stat *st_copy = memdup(&cf->st, sizeof(cf->st));
+ if (!st_copy)
+ return log_oom();
+
+ if (set_ensure_consume(stats, &inode_unmodified_hash_ops, TAKE_PTR(st_copy)) < 0)
+ return log_oom();
+
+ _cleanup_fclose_ FILE *f = NULL;
+ r = xfopenat(cf->fd, /* path= */ NULL, "re", /* open_flags= */ 0, &f);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to open '%s', skipping: %m", cf->result);
+ return 0;
+ }
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
+ unsigned line = 0, column = 0;
+ r = sd_json_parse_file(f, cf->result, /* flags= */ 0, &j, &line, &column);
+ if (r < 0) {
+ if (line > 0)
+ log_syntax(/* unit= */ NULL, LOG_WARNING, cf->result, line, r, "Failed to parse JSON, skipping: %m");
+ else
+ log_warning_errno(r, "Failed to parse JSON file '%s', skipping: %m", cf->result);
+ return 0;
+ }
+
+ if (sd_json_variant_is_array(j)) {
+ sd_json_variant *i;
+ int ret = 0;
+ JSON_VARIANT_ARRAY_FOREACH(i, j)
+ RET_GATHER(ret, load_static_record_file_item(i, records));
+ if (ret < 0)
+ return ret;
+ } else if (sd_json_variant_is_object(j)) {
+ r = load_static_record_file_item(j, records);
+ if (r < 0)
+ return r;
+ } else {
+ log_warning("JSON file '%s' contains neither array nor object, skipping.", cf->result);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int manager_static_records_read(Manager *m) {
+ int r;
+
+ usec_t ts;
+ assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &ts) >= 0);
+
+ /* See if we checked the static records db recently already */
+ if (m->static_records_last != USEC_INFINITY && usec_add(m->static_records_last, STATIC_RECORDS_RECHECK_USEC) > ts)
+ return 0;
+
+ m->static_records_last = ts;
+
+ ConfFile **files = NULL;
+ size_t n_files = 0;
+ CLEANUP_ARRAY(files, n_files, conf_file_free_many);
+
+ r = conf_files_list_nulstr_full(
+ ".rr",
+ /* root= */ NULL,
+ CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN,
+ CONF_PATHS_NULSTR("systemd/resolve/static.d/"),
+ &files,
+ &n_files);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate static record drop-ins: %m");
+
+ /* Let's suppress reloads if nothing changed. For that keep the set of inodes from the previous
+ * reload around, and see if there are any changes on them. */
+ bool reload;
+ if (set_size(m->static_records_stat) != n_files)
+ reload = true;
+ else {
+ reload = false;
+ FOREACH_ARRAY(f, files, n_files)
+ if (!set_contains(m->static_records_stat, &(*f)->st)) {
+ reload = true;
+ break;
+ }
+ }
+
+ if (!reload) {
+ log_debug("No static record files changed, not re-reading.");
+ return 0;
+ }
+
+ _cleanup_(hashmap_freep) Hashmap *records = NULL;
+ _cleanup_(set_freep) Set *stats = NULL;
+ FOREACH_ARRAY(f, files, n_files)
+ (void) load_static_record_file(*f, &records, &stats);
+
+ hashmap_free(m->static_records);
+ m->static_records = TAKE_PTR(records);
+
+ set_free(m->static_records_stat);
+ m->static_records_stat = TAKE_PTR(stats);
+
+ return 0;
+}
+
+int manager_static_records_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(q);
+ assert(answer);
+
+ if (!m->read_static_records)
+ return 0;
+
+ (void) manager_static_records_read(m);
+
+ const char *n = dns_question_first_name(q);
+ if (!n)
+ return 0;
+
+ DnsAnswer *f = hashmap_get(m->static_records, n);
+ if (!f)
+ return 0;
+
+ r = dns_answer_extend(answer, f);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+void manager_static_records_flush(Manager *m) {
+ assert(m);
+
+ m->static_records = hashmap_free(m->static_records);
+ m->static_records_stat = set_free(m->static_records_stat);
+ m->static_records_last = USEC_INFINITY;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "resolved-forward.h"
+
+void manager_static_records_flush(Manager *m);
+int manager_static_records_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
#DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes
+#ReadStaticRecords=yes
#ResolveUnicastSingleLabel=no
#StaleRetentionSec=0
#RefuseRecordTypes=
grep -qF "1.2.3.4" "$RUN_OUT"
}
+testcase_static_record() {
+ mkdir -p /run/systemd/resolve/static.d/
+ cat >/run/systemd/resolve/static.d/statictest.rr <<EOF
+{
+ "key": { "name" : "statictest.waldo", "type" : 1 },
+ "address" : [ 5, 7, 9, 11 ]
+}
+EOF
+ cat >/run/systemd/resolve/static.d/statictest2.rr <<EOF
+[
+ {
+ "key": { "name" : "statictest2.waldo", "type" : 1 },
+ "address" : [ 5, 7, 9, 12 ]
+ },
+ {
+ "key": { "name" : "statictest2.waldo", "type" : 28 },
+ "address" : [ 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 12 ]
+ }
+]
+EOF
+ cat >/run/systemd/resolve/static.d/garbage.rr <<EOF
+[
+ {
+ "key": { "name" : "invalid...domain", "type" : 1 },
+ "address" : [ 5, 7, 9, 12 ]
+ },
+]
+EOF
+ cat >/run/systemd/resolve/static.d/garbage2.rr <<EOF
+[
+ {
+ "key": { "name" : "piff", "type" : 9 },
+ },
+]
+EOF
+
+ systemctl reload systemd-resolved
+
+ run resolvectl query statictest.waldo
+ grep -qF 5.7.9.11 "$RUN_OUT"
+
+ run resolvectl query statictest2.waldo
+ grep -qF 5.7.9.12 "$RUN_OUT"
+ grep -qF a0b:a0b:a0b:a0b:a0b:a0b:a0b:a0c "$RUN_OUT"
+
+ rm /run/systemd/resolve/static.d/statictest*.rr /run/systemd/resolve/static.d/garbage*.rr
+ systemctl reload systemd-resolved
+}
+
# PRE-SETUP
systemctl unmask systemd-resolved.service
systemctl enable --now systemd-resolved.service