resolvconf: reject malformed stdin without changing link state

The resolvconf compatibility parser ignored errors from
nameserver, domain, and search lines. If one of those lines was
malformed, resolvectl could still send the partially parsed
configuration to resolved and overwrite the previous link state.

Reproducer:

  IF=enp1s0
  TMP="$(mktemp -d)"

  ln -s "$(command -v resolvectl)" "$TMP/resolvconf"

  sudo resolvectl dns "$IF" 1.1.1.1
  sudo resolvectl domain "$IF" keep.example
  resolvectl domain "$IF"

  printf 'nameserver 1.1.1.1\nsearch "unterminated\n' |
    sudo "$TMP/resolvconf" -a "$IF" || true

  resolvectl domain "$IF"

Before:

  The malformed search line was ignored and the previous domain
  was cleared:

    Link 2 (enp1s0): keep.example
    Link 2 (enp1s0):

After:

  The malformed input fails before updating resolved, so
  keep.example remains configured.

Follow-up for: 097f130731
This commit is contained in:
dongshengyuan
2026-08-13 17:40:50 +08:00
parent e7223f2ab2
commit 253b34d8ac
4 changed files with 14 additions and 3 deletions
+3
View File
@@ -454,6 +454,9 @@ CHANGES WITH 261:
Changes in systemd-resolved:
* The resolvconf compatibility mode now rejects malformed stdin input
and leaves the existing per-link DNS configuration unchanged.
* systemd-resolved will now read additional DNS resource record
definitions to resolve locally from JSON drop-in files in
{/etc,/run,/usr/local/lib,/usr/lib}/systemd/resolve/static.d/. This
+2 -1
View File
@@ -564,7 +564,8 @@
DNS configuration data from its standard input. Relevant fields are <literal>nameserver</literal> and
<literal>domain</literal>/<literal>search</literal>. This command is mostly identical to invoking
<command>resolvectl</command> with a combination of <option>dns</option> and <option>domain</option>
commands.</para>
commands. If a relevant input line has invalid syntax, the command fails and leaves the existing
per-interface configuration unchanged.</para>
<xi:include href="version-info.xml" xpointer="v239"/></listitem>
</varlistentry>
+6 -2
View File
@@ -116,7 +116,9 @@ static int parse_stdin(LookupType lookup_type) {
a = first_word(line, "nameserver");
if (a) {
(void) parse_nameserver(a);
r = parse_nameserver(a);
if (r < 0)
return log_error_errno(r, "Failed to parse resolv.conf nameserver line: %m");
continue;
}
@@ -124,7 +126,9 @@ static int parse_stdin(LookupType lookup_type) {
if (!a)
a = first_word(line, "search");
if (a) {
(void) parse_search_domain(a);
r = parse_search_domain(a);
if (r < 0)
return log_error_errno(r, "Failed to parse resolv.conf domain/search line: %m");
continue;
}
+3
View File
@@ -273,6 +273,9 @@ manual_testcase_01_resolvectl() {
assert_in 'test-domain1.example.com' "$(resolvectl domain hoge)"
assert_in 'test-domain2.example.com' "$(resolvectl domain hoge)"
assert_in 'test-search-domain.example.com' "$(resolvectl domain hoge)"
resolvectl domain hoge keep.example
(! echo -e "nameserver 10.0.2.1\nsearch \"unterminated" | SYSTEMD_INVOKED_AS=resolvconf resolvectl -a hoge)
assert_in 'keep.example' "$(resolvectl domain hoge)"
# Tests for 'resolvconf -x'
echo nameserver 10.0.2.1 | SYSTEMD_INVOKED_AS=resolvconf resolvectl -x -a hoge