diff --git a/NEWS b/NEWS index ccadaa256d..c5686b929f 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/man/resolvectl.xml b/man/resolvectl.xml index b7a28b47bb..fabd64e81e 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -482,6 +482,10 @@ types: SSHFP, TLSA, OPENPGPKEY where this dumps the key material only; and A, AAAA where this dumps the address data. + This option may only be used with or an RFC 4501 + dns: URI, which both perform DNS resource record lookups. It may not be combined + with regular hostname or address lookups. + @@ -560,7 +564,8 @@ DNS configuration data from its standard input. Relevant fields are nameserver and domain/search. This command is mostly identical to invoking resolvectl with a combination of and - commands. + commands. If a relevant input line has invalid syntax, the command fails and leaves the existing + per-interface configuration unchanged. diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c index d3ddec755a..dcac5c130e 100644 --- a/src/resolve/resolvconf-compat.c +++ b/src/resolve/resolvconf-compat.c @@ -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; } diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 5e51664763..3501af1c81 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -71,6 +71,7 @@ static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */ static const char *arg_service_family = NULL; +static bool arg_service_txt_set = false; static bool arg_ask_password = true; typedef enum RawType { @@ -893,6 +894,10 @@ static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { int family, ifindex; union in_addr_union a; + if (arg_raw != RAW_NONE) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--raw may only be combined with --type= or dns: URIs."); + r = in_addr_ifindex_from_string_auto(*p, &family, &a, &ifindex); if (r >= 0) RET_GATHER(ret, resolve_address(family, &a, ifindex)); @@ -904,7 +909,7 @@ static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { return ret; } -static int resolve_service(const char *name, const char *type, const char *domain) { +static int resolve_service(const char *name, const char *type, const char *domain, uint64_t flags) { int r; assert(domain); @@ -938,7 +943,7 @@ static int resolve_service(const char *name, const char *type, const char *domai JSON_BUILD_PAIR_STRING_NON_EMPTY("type", type), JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex), JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_family != AF_UNSPEC, "family", arg_family), - JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", flags)); if (r < 0) return log_error_errno(r, "Failed to issue varlink call: %m"); @@ -1019,15 +1024,20 @@ static int resolve_service(const char *name, const char *type, const char *domai VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN", 2, 4, 0, "Resolve service (SRV)"); static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) { + uint64_t flags = arg_flags; + if (sd_json_format_enabled(arg_json_format_flags)) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format."); + if (argc < 4 && !arg_service_txt_set) + flags |= SD_RESOLVED_NO_TXT; + if (argc == 2) - return resolve_service(NULL, NULL, argv[1]); - else if (argc == 3) - return resolve_service(NULL, argv[1], argv[2]); - else - return resolve_service(argv[1], argv[2], argv[3]); + return resolve_service(NULL, NULL, argv[1], flags); + if (argc == 3) + return resolve_service(NULL, argv[1], argv[2], flags); + + return resolve_service(argv[1], argv[2], argv[3], flags); } #if HAVE_OPENSSL @@ -3363,6 +3373,7 @@ static int compat_parse_argv(int argc, char *argv[], char ***remaining_args) { if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); + arg_service_txt_set = true; break; OPTION_LONG("openpgp", NULL, "Query OpenPGP public key"): @@ -3597,6 +3608,7 @@ static int native_parse_argv(int argc, char *argv[], char ***remaining_args) { if (r < 0) return r; SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); + arg_service_txt_set = true; break; OPTION_LONG("cname", "BOOL", "Follow CNAME redirects (default: yes)"): diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh index 44cc71ba9f..172dff245d 100755 --- a/test/units/TEST-75-RESOLVED.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -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 @@ -580,6 +583,10 @@ testcase_08_resolved() { run resolvectl query signed.test grep -qF "signed.test: 10.0.0.10" "$RUN_OUT" grep -qF "authenticated: yes" "$RUN_OUT" + (! run resolvectl --raw query localhost) + grep -qF -- "--raw may only be combined with --type= or dns: URIs." "$RUN_OUT" + resolvectl --raw=packet query --type=A signed.test >"$RUN_OUT" + test -s "$RUN_OUT" run dig @ns1.unsigned.test +short MX signed.test grep -qF "10 mail.signed.test." "$RUN_OUT" run resolvectl query --legend=no -t MX signed.test @@ -594,10 +601,13 @@ testcase_08_resolved() { # Check SRV support run resolvectl service _mysvc._tcp signed.test grep -qF "myservice.signed.test:1234" "$RUN_OUT" - grep -qF "This is TXT for myservice" "$RUN_OUT" + (! grep -qF "This is TXT for myservice" "$RUN_OUT") grep -qF "10.0.0.20" "$RUN_OUT" grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT" grep -qF "authenticated: yes" "$RUN_OUT" + run resolvectl service "" _mysvc._tcp signed.test + grep -qF "myservice.signed.test:1234" "$RUN_OUT" + grep -qF "This is TXT for myservice" "$RUN_OUT" # Test service resolve over Varlink run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}' @@ -1355,7 +1365,7 @@ testcase_14_refuse_record_types() { run resolvectl service _mysvc._tcp signed.test grep -qF "myservice.signed.test:1234" "$RUN_OUT" - grep -qF "This is TXT for myservice" "$RUN_OUT" + (! grep -qF "This is TXT for myservice" "$RUN_OUT") grep -qF "10.0.0.20" "$RUN_OUT" (! grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT") grep -qF "authenticated: yes" "$RUN_OUT" @@ -1377,7 +1387,7 @@ testcase_14_refuse_record_types() { run resolvectl service _mysvc._tcp signed.test grep -qF "myservice.signed.test:1234" "$RUN_OUT" - grep -qF "This is TXT for myservice" "$RUN_OUT" + (! grep -qF "This is TXT for myservice" "$RUN_OUT") (! grep -qF "10.0.0.20" "$RUN_OUT") (! grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT") grep -qF "authenticated: yes" "$RUN_OUT"