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);
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));
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);
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);
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;
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);
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;
"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"
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:"));
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),
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;
}
#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, '@');
log_debug("Looking up \"%s\".", full);
r = resolve_record(
- bus,
full,
arg_class ?: DNS_CLASS_IN,
arg_type ?: DNS_TYPE_OPENPGPKEY,
log_debug("Looking up \"%s\".", full);
return resolve_record(
- bus,
full,
arg_class ?: DNS_CLASS_IN,
arg_type ?: DNS_TYPE_OPENPGPKEY,
"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
#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, ':');
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);
}
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.");
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;
}
#include "sd-json.h"
#include "dns-packet.h"
+#include "iovec-util.h"
#include "json-util.h"
#include "resolve-varlink-util.h"
*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;
+}