From: Nick Rosbrook Date: Fri, 19 Jun 2026 19:01:01 +0000 (-0400) Subject: resolvectl: use varlink instead of dbus for ResolveRecord X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cfac7ec169892b387447245d0aa81e712639f11c;p=thirdparty%2Fsystemd.git resolvectl: use varlink instead of dbus for ResolveRecord --- diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 2cfd5855649..327cf101407 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -279,7 +279,7 @@ static void print_ifindex_comment(int printed_so_far, int ifindex) { ansi_grey(), ifname, ansi_normal()); } -static int varlink_log_resolve_error(const char *name, const char *error_id, sd_json_variant *reply) { +static int varlink_log_resolve_error(const char *name, const char *error_id, sd_json_variant *reply, bool warn_missing) { int r; assert(name); @@ -342,9 +342,13 @@ static int varlink_log_resolve_error(const char *name, const char *error_id, sd_ return log_error_errno(ret, "%s: resolve call failed: DNSSEC validation failed: %s%s", name, error.result, strempty(msg_extended)); if (error.rcode != _DNS_RCODE_INVALID) { - if (error.rcode == DNS_RCODE_NXDOMAIN) + if (error.rcode == DNS_RCODE_NXDOMAIN) { + if (!warn_missing) + return -ENXIO; + return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "%s: resolve call failed: Name '%s' not found%s%s", name, error.query_string ?: name, error.ede_rcode >= 0 ? ":" : "", strempty(msg_extended)); + } return log_error_errno(ret, "%s: resolve call failed: Could not resolve '%s', server or network returned error: %s%s", name, error.query_string ?: name, FORMAT_DNS_RCODE(error.rcode), strempty(msg_extended)); @@ -403,7 +407,7 @@ static int resolve_host(const char *name) { ts = now(CLOCK_MONOTONIC) - ts; if (!isempty(error_id)) - return varlink_log_resolve_error(name, error_id, v); + return varlink_log_resolve_error(name, error_id, v, /* warn_missing = */ true); _cleanup_(resolve_hostname_reply_done) ResolveHostnameReply reply = {}; r = dispatch_resolve_hostname_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); @@ -489,7 +493,7 @@ static int resolve_address(int family, const union in_addr_union *address, int i ts = now(CLOCK_MONOTONIC) - ts; if (!isempty(error_id)) - return varlink_log_resolve_error(pretty, error_id, v); + return varlink_log_resolve_error(pretty, error_id, v, /* warn_missing = */ true); _cleanup_(resolve_address_reply_done) ResolveAddressReply reply = {}; r = dispatch_resolve_address_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); @@ -520,15 +524,10 @@ static int resolve_address(int family, const union in_addr_union *address, int i return 0; } -static int output_rr_packet(const void *d, size_t l, int ifindex) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; +static int output_rr_packet(DnsResourceRecord *rr, int ifindex) { int r; - assert(d || l == 0); - - r = dns_resource_record_new_from_raw(&rr, d, l); - if (r < 0) - return log_error_errno(r, "Failed to parse RR: %m"); + assert(rr); if (sd_json_format_enabled(arg_json_format_flags)) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; @@ -611,14 +610,7 @@ static bool single_label_nonsynthetic(const char *name) { return !streq(name, first_label); } -static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *idnafied = NULL; - bool needs_authentication = false; - unsigned n = 0; - uint64_t flags; - usec_t ts; +static int resolve_record(const char *name, uint16_t class, uint16_t type, bool warn_missing) { int r; assert(name); @@ -629,6 +621,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ log_notice("(Note that search domains are not appended when --type= is specified. " "Please specify fully qualified domain names, or remove --type= switch from invocation in order to request regular hostname resolution.)"); + _cleanup_free_ char *idnafied = NULL; r = idna_candidate(name, &idnafied); if (r < 0) return r; @@ -637,85 +630,76 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ "Please specify translated domain names — i.e. '%s' — when resolving raw records, or remove --type= switch from invocation in order to request regular hostname resolution.", idnafied); - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveRecord"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + r = varlink_connect_with_query_timeout(&vl); if (r < 0) - return bus_log_create_error(r); - - ts = now(CLOCK_MONOTONIC); - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - if (warn_missing || r != -ENXIO) - log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); return r; - } - ts = now(CLOCK_MONOTONIC) - ts; + usec_t ts = now(CLOCK_MONOTONIC); - r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); + const char *error_id = NULL; + sd_json_variant *v = NULL; + r = sd_varlink_callbo( + vl, + "io.systemd.Resolve.ResolveRecord", + &v, + &error_id, + SD_JSON_BUILD_PAIR_STRING("name", name), + SD_JSON_BUILD_PAIR_UNSIGNED("type", type), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("class", class), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); if (r < 0) - return bus_log_parse_error(r); + return log_error_errno(r, "Failed to issue varlink call: %m"); - while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { - uint16_t c, t; - int ifindex; - const void *d; - size_t l; + ts = now(CLOCK_MONOTONIC) - ts; - assert_cc(sizeof(int) == sizeof(int32_t)); + if (!isempty(error_id)) { + if (streq(error_id, "io.systemd.Resolve.ResourceRecordTypeInvalidForQuery")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified resource record type %" PRIu16 " may not be used in a query", type); - r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); - if (r < 0) - return bus_log_parse_error(r); + if (streq(error_id, "io.systemd.Resolve.ResourceRecordTypeObsolete")) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Specified DNS resource record type %" PRIu16 " is obsolete", type); - r = sd_bus_message_read_array(reply, 'y', &d, &l); - if (r < 0) - return bus_log_parse_error(r); + return varlink_log_resolve_error(name, error_id, v, warn_missing); + } - r = sd_bus_message_exit_container(reply); + _cleanup_(resolve_record_reply_done) ResolveRecordReply reply = {}; + r = dispatch_resolve_record_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); + if (r < 0) + return r; + + bool needs_authentication = false; + FOREACH_ARRAY(record, reply.records, reply.n_records) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + r = dns_resource_record_new_from_raw(&rr, record->raw.iov_base, record->raw.iov_len); if (r < 0) - return bus_log_parse_error(r); + return log_error_errno(r, "Failed to parse RR: %m"); if (arg_raw == RAW_PACKET) { - uint64_t u64 = htole64(l); + uint64_t u64 = htole64(record->raw.iov_len); fwrite(&u64, sizeof(u64), 1, stdout); - fwrite(d, 1, l, stdout); + fwrite(record->raw.iov_base, 1, record->raw.iov_len, stdout); } else { - r = output_rr_packet(d, l, ifindex); + r = output_rr_packet(rr, record->ifindex); if (r < 0) return r; } - if (dns_type_needs_authentication(t)) + if (dns_type_needs_authentication(rr->key->type)) needs_authentication = true; - - n++; } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(reply, "t", &flags); - if (r < 0) - return bus_log_parse_error(r); - if (n == 0) { + if (reply.n_records == 0) { if (warn_missing) log_error("%s: no records found", name); return -ESRCH; } - print_source(flags, ts); + print_source(reply.flags, ts); - if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { + if ((reply.flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { fflush(stdout); fprintf(stderr, "\n%s" @@ -730,12 +714,11 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ return 0; } -static int resolve_rfc4501(sd_bus *bus, const char *name) { +static int resolve_rfc4501(const char *name) { uint16_t type = 0, class = 0; const char *p, *q, *n; int r; - assert(bus); assert(name); assert(startswith(name, "dns:")); @@ -833,7 +816,7 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) { if (type == 0) type = arg_type ?: DNS_TYPE_A; - return resolve_record(bus, n, class, type, true); + return resolve_record(n, class, type, true); invalid: return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -843,21 +826,16 @@ invalid: VERB(verb_query, "query", "HOSTNAME|ADDRESS…", 2, VERB_ANY, 0, "Resolve domain names, IPv4 and IPv6 addresses"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int ret = 0, r; - r = acquire_bus(&bus); - if (r < 0) - return r; - if (arg_type != 0) STRV_FOREACH(p, strv_skip(argv, 1)) - RET_GATHER(ret, resolve_record(bus, *p, arg_class, arg_type, true)); + RET_GATHER(ret, resolve_record(*p, arg_class, arg_type, true)); else STRV_FOREACH(p, strv_skip(argv, 1)) { if (startswith(*p, "dns:")) - RET_GATHER(ret, resolve_rfc4501(bus, *p)); + RET_GATHER(ret, resolve_rfc4501(*p)); else { int family, ifindex; union in_addr_union a; @@ -1074,10 +1052,9 @@ static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) } #if HAVE_OPENSSL -static int resolve_openpgp(sd_bus *bus, const char *address) { +static int resolve_openpgp(const char *address) { int r; - assert(bus); assert(address); const char *domain = strrchr(address, '@'); @@ -1108,7 +1085,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { log_debug("Looking up \"%s\".", full); r = resolve_record( - bus, full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_OPENPGPKEY, @@ -1128,7 +1104,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { log_debug("Looking up \"%s\".", full); return resolve_record( - bus, full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_OPENPGPKEY, @@ -1140,18 +1115,13 @@ VERB(verb_openpgp, "openpgp", "EMAIL@DOMAIN…", 2, VERB_ANY, 0, "Query OpenPGP public key"); static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) { #if HAVE_OPENSSL - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r, ret = 0; - - r = acquire_bus(&bus); - if (r < 0) - return r; + int ret = 0; 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."); STRV_FOREACH(p, strv_skip(argv, 1)) - RET_GATHER(ret, resolve_openpgp(bus, *p)); + RET_GATHER(ret, resolve_openpgp(*p)); return ret; #else @@ -1159,13 +1129,12 @@ static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) #endif } -static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) { +static int resolve_tlsa(const char *family, const char *address) { const char *port; uint16_t port_num = 443; _cleanup_free_ char *full = NULL; int r; - assert(bus); assert(address); port = strrchr(address, ':'); @@ -1186,7 +1155,7 @@ static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) { log_debug("Looking up \"%s\".", full); - return resolve_record(bus, full, + return resolve_record(full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_TLSA, true); } @@ -1198,17 +1167,12 @@ static bool service_family_is_valid(const char *s) { VERB(verb_tlsa, "tlsa", "DOMAIN[:PORT]…", 2, VERB_ANY, 0, "Query TLS public key"); static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *family = "tcp"; char **args; - int r, ret = 0; + int ret = 0; assert(argc >= 2); - r = acquire_bus(&bus); - if (r < 0) - return r; - 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."); @@ -1219,7 +1183,7 @@ static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { args = strv_skip(argv, 1); STRV_FOREACH(p, args) - RET_GATHER(ret, resolve_tlsa(bus, family, *p)); + RET_GATHER(ret, resolve_tlsa(family, *p)); return ret; } diff --git a/src/shared/resolve-varlink-util.c b/src/shared/resolve-varlink-util.c index 877e298999a..c3a638f3ad2 100644 --- a/src/shared/resolve-varlink-util.c +++ b/src/shared/resolve-varlink-util.c @@ -3,6 +3,7 @@ #include "sd-json.h" #include "dns-packet.h" +#include "iovec-util.h" #include "json-util.h" #include "resolve-varlink-util.h" @@ -175,3 +176,74 @@ int dispatch_resolve_address_reply(const char *name, sd_json_variant *variant, s *ret = TAKE_STRUCT(reply); return 0; } + +static void resolved_record_done(ResolvedRecord *record) { + if (!record) + return; + + iovec_done(&record->raw); +} + +static int dispatch_resolved_record(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_record_dispatch_table[] = { + { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(ResolvedRecord, ifindex), SD_JSON_RELAX }, + { "raw", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_unbase64_iovec, offsetof(ResolvedRecord, raw), SD_JSON_MANDATORY }, + { "rr", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, + {}, + }; + ResolvedRecord *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolved_record_done) ResolvedRecord record = {}; + r = sd_json_dispatch(variant, resolved_record_dispatch_table, flags & ~SD_JSON_MANDATORY, &record); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(record); + return 0; +} + +static int dispatch_resolved_record_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolveRecordReply *reply = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(reply->records, reply->n_records + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_record(name, v, flags, &reply->records[reply->n_records++]); + if (r < 0) + return r; + } + + return 0; +} + +void resolve_record_reply_done(ResolveRecordReply *reply) { + if (!reply) + return; + + FOREACH_ARRAY(record, reply->records, reply->n_records) + resolved_record_done(record); + reply->records = mfree(reply->records); + reply->n_records = 0; +} + +int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_record_reply_dispatch_table[] = { + { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ResolveRecordReply, flags), SD_JSON_MANDATORY }, + { "rrs", SD_JSON_VARIANT_ARRAY, dispatch_resolved_record_array, 0, SD_JSON_MANDATORY }, + {}, + }; + ResolveRecordReply *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_record_reply_done) ResolveRecordReply reply = {}; + r = sd_json_dispatch(variant, resolve_record_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(reply); + return 0; +} diff --git a/src/shared/resolve-varlink-util.h b/src/shared/resolve-varlink-util.h index 9fbc8a1492b..58e8f4d9184 100644 --- a/src/shared/resolve-varlink-util.h +++ b/src/shared/resolve-varlink-util.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + #include "in-addr-util.h" #include "shared-forward.h" @@ -47,3 +49,18 @@ typedef struct ResolveAddressReply { void resolve_address_reply_done(ResolveAddressReply *reply); int dispatch_resolve_address_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); + +typedef struct ResolvedRecord { + int ifindex; + struct iovec raw; +} ResolvedRecord; + +typedef struct ResolveRecordReply { + uint64_t flags; + ResolvedRecord *records; + size_t n_records; +} ResolveRecordReply; + +void resolve_record_reply_done(ResolveRecordReply *reply); + +int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);