resolved: add ability to define additional local RRs via drop-ins
This is an extension of the /etc/hosts concept, but can provide any kind of RRs (well, actually, we only parse A/AAAA/PTR for now, but the concept is open for more). Fixes: #17791
This commit is contained in:
+23
-1
@@ -363,12 +363,33 @@ DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953</programlisting>
|
||||
<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),
|
||||
@@ -418,6 +439,7 @@ DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953</programlisting>
|
||||
<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>
|
||||
|
||||
@@ -1280,6 +1280,7 @@ manpages = [
|
||||
['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', [], ''],
|
||||
|
||||
@@ -131,6 +131,16 @@
|
||||
<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>,
|
||||
@@ -510,6 +520,7 @@ search foobar.com barbar.com
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?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>
|
||||
@@ -36,6 +36,7 @@ systemd_resolved_extract_sources = files(
|
||||
'resolved-mdns.c',
|
||||
'resolved-resolv-conf.c',
|
||||
'resolved-socket-graveyard.c',
|
||||
'resolved-static-records.c',
|
||||
'resolved-util.c',
|
||||
'resolved-varlink.c',
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#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"
|
||||
@@ -910,6 +911,33 @@ static int dns_query_try_etc_hosts(DnsQuery *q) {
|
||||
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;
|
||||
|
||||
@@ -1038,6 +1066,14 @@ int dns_query_go(DnsQuery *q) {
|
||||
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;
|
||||
|
||||
@@ -31,6 +31,7 @@ Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0,
|
||||
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)
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#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"
|
||||
@@ -637,6 +638,7 @@ static void manager_set_defaults(Manager *m) {
|
||||
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;
|
||||
@@ -660,6 +662,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
|
||||
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);
|
||||
|
||||
@@ -730,6 +733,7 @@ int manager_new(Manager **ret) {
|
||||
.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,
|
||||
@@ -918,6 +922,7 @@ Manager* manager_free(Manager *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);
|
||||
|
||||
@@ -123,6 +123,12 @@ typedef struct Manager {
|
||||
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;
|
||||
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* 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);
|
||||
@@ -39,6 +39,7 @@
|
||||
#DNSStubListener=yes
|
||||
#DNSStubListenerExtra=
|
||||
#ReadEtcHosts=yes
|
||||
#ReadStaticRecords=yes
|
||||
#ResolveUnicastSingleLabel=no
|
||||
#StaleRetentionSec=0
|
||||
#RefuseRecordTypes=
|
||||
|
||||
@@ -1487,6 +1487,55 @@ EOF
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user