DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(status_mode_json_field, StatusMode);
+static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
+ int r;
+
+ if (value) {
+ r = parse_boolean(value);
+ if (r >= 0)
+ return strv_extendf(strv, "%s%s", plus_minus(r), name);
+ }
+
+ return strv_extendf(strv, "%s=%s", name, value ?: "???");
+}
+
static int acquire_bus(sd_bus **ret) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
return ret;
}
-static int verb_show_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(table_unrefp) Table *table = NULL;
- sd_json_variant *reply = NULL;
+static int varlink_dump_dns_configuration(sd_json_variant **ret) {
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
+ sd_json_variant *v;
int r;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ assert(ret);
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve");
if (r < 0)
- return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
+ return log_error_errno(r, "Failed to connect to service /run/systemd/resolve/io.systemd.Resolve: %m");
- r = varlink_callbo_and_log(
- vl,
- "io.systemd.Resolve.Monitor.DumpStatistics",
- &reply,
- SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ r = varlink_call_and_log(vl, "io.systemd.Resolve.DumpDNSConfiguration", /* parameters= */ NULL, &reply);
if (r < 0)
return r;
- if (sd_json_format_enabled(arg_json_format_flags))
- return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
-
- struct statistics {
- sd_json_variant *transactions;
- sd_json_variant *cache;
- sd_json_variant *dnssec;
- } statistics;
-
- static const sd_json_dispatch_field statistics_dispatch_table[] = {
- { "transactions", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, transactions), SD_JSON_MANDATORY },
- { "cache", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, cache), SD_JSON_MANDATORY },
- { "dnssec", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, dnssec), SD_JSON_MANDATORY },
- {},
- };
-
- r = sd_json_dispatch(reply, statistics_dispatch_table, SD_JSON_LOG, &statistics);
- if (r < 0)
- return r;
+ v = sd_json_variant_by_key(reply, "configuration");
- struct transactions {
- uint64_t n_current_transactions;
- uint64_t n_transactions_total;
- uint64_t n_timeouts_total;
- uint64_t n_timeouts_served_stale_total;
- uint64_t n_failure_responses_total;
- uint64_t n_failure_responses_served_stale_total;
- } transactions;
+ if (!sd_json_variant_is_array(v))
+ return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "DumpDNSConfiguration() response missing 'configuration' key.");
- static const sd_json_dispatch_field transactions_dispatch_table[] = {
- { "currentTransactions", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_current_transactions), SD_JSON_MANDATORY },
- { "totalTransactions", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_transactions_total), SD_JSON_MANDATORY },
- { "totalTimeouts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_timeouts_total), SD_JSON_MANDATORY },
- { "totalTimeoutsServedStale", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_timeouts_served_stale_total), SD_JSON_MANDATORY },
- { "totalFailedResponses", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_failure_responses_total), SD_JSON_MANDATORY },
- { "totalFailedResponsesServedStale", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_failure_responses_served_stale_total), SD_JSON_MANDATORY },
- {},
- };
+ TAKE_PTR(reply);
+ *ret = sd_json_variant_ref(v);
+ return 0;
+}
- r = sd_json_dispatch(statistics.transactions, transactions_dispatch_table, SD_JSON_LOG, &transactions);
- if (r < 0)
- return r;
+static int status_json_filter_links(sd_json_variant **configuration, char **links) {
+ _cleanup_set_free_ Set *links_by_index = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ sd_json_variant *w;
+ int r;
- struct cache {
- uint64_t cache_size;
- uint64_t n_cache_hit;
- uint64_t n_cache_miss;
- } cache;
+ assert(configuration);
- static const sd_json_dispatch_field cache_dispatch_table[] = {
- { "size", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, cache_size), SD_JSON_MANDATORY },
- { "hits", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, n_cache_hit), SD_JSON_MANDATORY },
- { "misses", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, n_cache_miss), SD_JSON_MANDATORY },
- {},
- };
+ if (links)
+ STRV_FOREACH(ifname, links) {
+ int ifindex = rtnl_resolve_interface_or_warn(/* rtnl= */ NULL, *ifname);
+ if (ifindex < 0)
+ return ifindex;
- r = sd_json_dispatch(statistics.cache, cache_dispatch_table, SD_JSON_LOG, &cache);
- if (r < 0)
- return r;
+ r = set_ensure_put(&links_by_index, NULL, INT_TO_PTR(ifindex));
+ if (r < 0)
+ return r;
+ }
- struct dnsssec {
- uint64_t n_dnssec_secure;
- uint64_t n_dnssec_insecure;
- uint64_t n_dnssec_bogus;
- uint64_t n_dnssec_indeterminate;
- } dnsssec;
+ JSON_VARIANT_ARRAY_FOREACH(w, *configuration) {
+ int ifindex = sd_json_variant_unsigned(sd_json_variant_by_key(w, "ifindex"));
- static const sd_json_dispatch_field dnssec_dispatch_table[] = {
- { "secure", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_secure), SD_JSON_MANDATORY },
- { "insecure", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_insecure), SD_JSON_MANDATORY },
- { "bogus", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_bogus), SD_JSON_MANDATORY },
- { "indeterminate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_indeterminate), SD_JSON_MANDATORY },
- {},
- };
+ if (links_by_index) {
+ if (ifindex <= 0)
+ /* Possibly invalid, but most likely unset because this is global
+ * or delegate configuration. */
+ continue;
- r = sd_json_dispatch(statistics.dnssec, dnssec_dispatch_table, SD_JSON_LOG, &dnsssec);
- if (r < 0)
- return r;
+ if (!set_contains(links_by_index, INT_TO_PTR(ifindex)))
+ continue;
- table = table_new_vertical();
- if (!table)
- return log_oom();
+ } else if (ifindex == LOOPBACK_IFINDEX)
+ /* By default, exclude the loopback interface. */
+ continue;
- r = table_add_many(table,
- TABLE_STRING, "Transactions",
- TABLE_SET_COLOR, ansi_highlight(),
- TABLE_SET_ALIGN_PERCENT, 0,
- TABLE_EMPTY,
- TABLE_FIELD, "Current Transactions",
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_UINT64, transactions.n_current_transactions,
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_FIELD, "Total Transactions",
- TABLE_UINT64, transactions.n_transactions_total,
- TABLE_EMPTY, TABLE_EMPTY,
- TABLE_STRING, "Cache",
- TABLE_SET_COLOR, ansi_highlight(),
- TABLE_SET_ALIGN_PERCENT, 0,
- TABLE_EMPTY,
- TABLE_FIELD, "Current Cache Size",
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_UINT64, cache.cache_size,
- TABLE_FIELD, "Cache Hits",
- TABLE_UINT64, cache.n_cache_hit,
- TABLE_FIELD, "Cache Misses",
- TABLE_UINT64, cache.n_cache_miss,
- TABLE_EMPTY, TABLE_EMPTY,
- TABLE_STRING, "Failure Transactions",
- TABLE_SET_COLOR, ansi_highlight(),
- TABLE_SET_ALIGN_PERCENT, 0,
- TABLE_EMPTY,
- TABLE_FIELD, "Total Timeouts",
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_UINT64, transactions.n_timeouts_total,
- TABLE_FIELD, "Total Timeouts (Stale Data Served)",
- TABLE_UINT64, transactions.n_timeouts_served_stale_total,
- TABLE_FIELD, "Total Failure Responses",
- TABLE_UINT64, transactions.n_failure_responses_total,
- TABLE_FIELD, "Total Failure Responses (Stale Data Served)",
- TABLE_UINT64, transactions.n_failure_responses_served_stale_total,
- TABLE_EMPTY, TABLE_EMPTY,
- TABLE_STRING, "DNSSEC Verdicts",
- TABLE_SET_COLOR, ansi_highlight(),
- TABLE_SET_ALIGN_PERCENT, 0,
- TABLE_EMPTY,
- TABLE_FIELD, "Secure",
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_UINT64, dnsssec.n_dnssec_secure,
- TABLE_FIELD, "Insecure",
- TABLE_UINT64, dnsssec.n_dnssec_insecure,
- TABLE_FIELD, "Bogus",
- TABLE_UINT64, dnsssec.n_dnssec_bogus,
- TABLE_FIELD, "Indeterminate",
- TABLE_UINT64, dnsssec.n_dnssec_indeterminate
- );
- if (r < 0)
- return table_log_add_error(r);
+ r = sd_json_variant_append_array(&v, w);
+ if (r < 0)
+ return r;
+ }
- return table_print_or_warn(table);
+ JSON_VARIANT_REPLACE(*configuration, TAKE_PTR(v));
+ return 0;
}
-static int verb_reset_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) {
- sd_json_variant *reply = NULL;
- _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+static int status_json_filter_fields(sd_json_variant **configuration, StatusMode mode) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ sd_json_variant *w;
+ const char *field;
int r;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
-
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
- if (r < 0)
- return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
+ assert(configuration);
- r = varlink_callbo_and_log(
- vl,
- "io.systemd.Resolve.Monitor.ResetStatistics",
- &reply,
- SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
- if (r < 0)
- return r;
+ field = status_mode_json_field_to_string(mode);
+ if (!field)
+ /* Nothing to filter for this mode. */
+ return 0;
- if (sd_json_format_enabled(arg_json_format_flags))
- return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
+ JSON_VARIANT_ARRAY_FOREACH(w, *configuration) {
+ /* Always include identifier fields like ifname or delegate, and include the requested
+ * field even if it is empty in the configuration. */
+ r = sd_json_variant_append_arraybo(
+ &v,
+ JSON_BUILD_PAIR_VARIANT_NON_NULL("ifname", sd_json_variant_by_key(w, "ifname")),
+ JSON_BUILD_PAIR_VARIANT_NON_NULL("ifindex", sd_json_variant_by_key(w, "ifindex")),
+ JSON_BUILD_PAIR_VARIANT_NON_NULL("delegate", sd_json_variant_by_key(w, "delegate")),
+ SD_JSON_BUILD_PAIR_VARIANT(field, sd_json_variant_by_key(w, field)));
+ if (r < 0)
+ return r;
+ }
+ JSON_VARIANT_REPLACE(*configuration, TAKE_PTR(v));
return 0;
}
-static int verb_flush_caches(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int format_dns_server_one(DNSConfiguration *configuration, DNSServer *s, char **ret) {
+ bool global;
int r;
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
-
- r = bus_call_method(bus, bus_resolve_mgr, "FlushCaches", &error, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r));
+ assert(s);
+ assert(ret);
- return 0;
-}
-
-static int verb_reset_server_features(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
-
- r = bus_call_method(bus, bus_resolve_mgr, "ResetServerFeatures", &error, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r));
-
- return 0;
-}
-
-static int status_print_strv(DNSConfiguration *c, char **p) {
- const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
- int pos1, pos2;
-
- assert(c);
-
- if (c->ifname)
- printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, c->ifindex, c->ifname, &pos2, ansi_normal());
- else if (c->delegate)
- printf("%s%nDelegate %s%n%s:", ansi_highlight(), &pos1, c->delegate, &pos2, ansi_normal());
- else
- printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
-
- size_t cols = columns(), position = pos2 - pos1 + 2;
-
- STRV_FOREACH(i, p) {
- size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
- * If that happens, we'll just print one item per line. */
-
- if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
- printf(" %s", *i);
- position = size_add(size_add(position, 1), our_len);
- } else {
- printf("\n%*s%s", (int) indent, "", *i);
- position = size_add(our_len, indent);
- }
- }
-
- printf("\n");
-
- return 0;
-}
-
-static void status_print_string(DNSConfiguration *c, const char *p) {
- assert(c);
-
- if (c->ifname)
- printf("%sLink %i (%s)%s: %s\n",
- ansi_highlight(),
- c->ifindex,
- c->ifname,
- ansi_normal(),
- p);
- else if (c->delegate)
- printf("%sDelegate %s%s: %s\n",
- ansi_highlight(),
- c->delegate,
- ansi_normal(),
- p);
- else
- printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(), p);
-}
-
-static void status_print_header(DNSConfiguration *c) {
- assert(c);
-
- if (c->ifname)
- printf("%sLink %i (%s)%s\n",
- ansi_highlight(),
- c->ifindex,
- c->ifname,
- ansi_normal());
- else if (c->delegate)
- printf("%sDelegate %s%s\n",
- ansi_highlight(),
- c->delegate,
- ansi_normal());
- else
- printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
-}
-
-static int dump_list(Table *table, const char *field, char * const *l) {
- int r;
-
- if (strv_isempty(l))
- return 0;
-
- r = table_add_many(table,
- TABLE_FIELD, field,
- TABLE_STRV_WRAPPED, l);
- if (r < 0)
- return table_log_add_error(r);
-
- return 0;
-}
-
-static int strv_extend_extended_bool(char ***strv, const char *name, const char *value) {
- int r;
-
- if (value) {
- r = parse_boolean(value);
- if (r >= 0)
- return strv_extendf(strv, "%s%s", plus_minus(r), name);
- }
-
- return strv_extendf(strv, "%s=%s", name, value ?: "???");
-}
-
-static int status_json_filter_fields(sd_json_variant **configuration, StatusMode mode) {
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- sd_json_variant *w;
- const char *field;
- int r;
-
- assert(configuration);
-
- field = status_mode_json_field_to_string(mode);
- if (!field)
- /* Nothing to filter for this mode. */
- return 0;
-
- JSON_VARIANT_ARRAY_FOREACH(w, *configuration) {
- /* Always include identifier fields like ifname or delegate, and include the requested
- * field even if it is empty in the configuration. */
- r = sd_json_variant_append_arraybo(
- &v,
- JSON_BUILD_PAIR_VARIANT_NON_NULL("ifname", sd_json_variant_by_key(w, "ifname")),
- JSON_BUILD_PAIR_VARIANT_NON_NULL("ifindex", sd_json_variant_by_key(w, "ifindex")),
- JSON_BUILD_PAIR_VARIANT_NON_NULL("delegate", sd_json_variant_by_key(w, "delegate")),
- SD_JSON_BUILD_PAIR_VARIANT(field, sd_json_variant_by_key(w, field)));
- if (r < 0)
- return r;
- }
-
- JSON_VARIANT_REPLACE(*configuration, TAKE_PTR(v));
- return 0;
-}
-
-static int status_json_filter_links(sd_json_variant **configuration, char **links) {
- _cleanup_set_free_ Set *links_by_index = NULL;
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- sd_json_variant *w;
- int r;
-
- assert(configuration);
-
- if (links)
- STRV_FOREACH(ifname, links) {
- int ifindex = rtnl_resolve_interface_or_warn(/* rtnl= */ NULL, *ifname);
- if (ifindex < 0)
- return ifindex;
-
- r = set_ensure_put(&links_by_index, NULL, INT_TO_PTR(ifindex));
- if (r < 0)
- return r;
- }
-
- JSON_VARIANT_ARRAY_FOREACH(w, *configuration) {
- int ifindex = sd_json_variant_unsigned(sd_json_variant_by_key(w, "ifindex"));
-
- if (links_by_index) {
- if (ifindex <= 0)
- /* Possibly invalid, but most likely unset because this is global
- * or delegate configuration. */
- continue;
-
- if (!set_contains(links_by_index, INT_TO_PTR(ifindex)))
- continue;
-
- } else if (ifindex == LOOPBACK_IFINDEX)
- /* By default, exclude the loopback interface. */
- continue;
-
- r = sd_json_variant_append_array(&v, w);
- if (r < 0)
- return r;
- }
-
- JSON_VARIANT_REPLACE(*configuration, TAKE_PTR(v));
- return 0;
-}
-
-static int varlink_dump_dns_configuration(sd_json_variant **ret) {
- _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
- sd_json_variant *v;
- int r;
-
- assert(ret);
-
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve");
- if (r < 0)
- return log_error_errno(r, "Failed to connect to service /run/systemd/resolve/io.systemd.Resolve: %m");
-
- r = varlink_call_and_log(vl, "io.systemd.Resolve.DumpDNSConfiguration", /* parameters= */ NULL, &reply);
- if (r < 0)
- return r;
-
- v = sd_json_variant_by_key(reply, "configuration");
-
- if (!sd_json_variant_is_array(v))
- return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "DumpDNSConfiguration() response missing 'configuration' key.");
-
- TAKE_PTR(reply);
- *ret = sd_json_variant_ref(v);
- return 0;
-}
-
-static int format_dns_server_one(DNSConfiguration *configuration, DNSServer *s, char **ret) {
- bool global;
- int r;
-
- assert(s);
- assert(ret);
-
- global = !(configuration->ifindex > 0 || configuration->delegate);
+ global = !(configuration->ifindex > 0 || configuration->delegate);
if (global && s->ifindex > 0 && s->ifindex != LOOPBACK_IFINDEX) {
/* This one has an (non-loopback) ifindex set, and we were told to suppress those. Hence do so. */
return 0;
}
-static int print_configuration(DNSConfiguration *configuration, StatusMode mode, bool *empty_line) {
- _cleanup_(table_unrefp) Table *table = NULL;
- int r;
-
- assert(configuration);
-
- pager_open(arg_pager_flags);
+static void status_print_header(DNSConfiguration *c) {
+ assert(c);
- bool global = !(configuration->ifindex > 0 || configuration->delegate);
- if (mode == STATUS_DNS) {
- _cleanup_strv_free_ char **l = NULL;
- r = format_dns_servers(configuration, configuration->dns_servers, &l);
- if (r < 0)
- return r;
+ if (c->ifname)
+ printf("%sLink %i (%s)%s\n",
+ ansi_highlight(),
+ c->ifindex,
+ c->ifname,
+ ansi_normal());
+ else if (c->delegate)
+ printf("%sDelegate %s%s\n",
+ ansi_highlight(),
+ c->delegate,
+ ansi_normal());
+ else
+ printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
+}
- return status_print_strv(configuration, l);
+static void status_print_string(DNSConfiguration *c, const char *p) {
+ assert(c);
- } else if (mode == STATUS_DOMAIN) {
- _cleanup_strv_free_ char **l = NULL;
- r = format_search_domains(configuration, configuration->search_domains, &l);
- if (r < 0)
+ if (c->ifname)
+ printf("%sLink %i (%s)%s: %s\n",
+ ansi_highlight(),
+ c->ifindex,
+ c->ifname,
+ ansi_normal(),
+ p);
+ else if (c->delegate)
+ printf("%sDelegate %s%s: %s\n",
+ ansi_highlight(),
+ c->delegate,
+ ansi_normal(),
+ p);
+ else
+ printf("%sGlobal%s: %s\n", ansi_highlight(), ansi_normal(), p);
+}
+
+static int status_print_strv(DNSConfiguration *c, char **p) {
+ const unsigned indent = strlen("Global: "); /* Use the same indentation everywhere to make things nice */
+ int pos1, pos2;
+
+ assert(c);
+
+ if (c->ifname)
+ printf("%s%nLink %i (%s)%n%s:", ansi_highlight(), &pos1, c->ifindex, c->ifname, &pos2, ansi_normal());
+ else if (c->delegate)
+ printf("%s%nDelegate %s%n%s:", ansi_highlight(), &pos1, c->delegate, &pos2, ansi_normal());
+ else
+ printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal());
+
+ size_t cols = columns(), position = pos2 - pos1 + 2;
+
+ STRV_FOREACH(i, p) {
+ size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen).
+ * If that happens, we'll just print one item per line. */
+
+ if (position <= indent || size_add(size_add(position, 1), our_len) < cols) {
+ printf(" %s", *i);
+ position = size_add(size_add(position, 1), our_len);
+ } else {
+ printf("\n%*s%s", (int) indent, "", *i);
+ position = size_add(our_len, indent);
+ }
+ }
+
+ printf("\n");
+
+ return 0;
+}
+
+static int dump_list(Table *table, const char *field, char * const *l) {
+ int r;
+
+ if (strv_isempty(l))
+ return 0;
+
+ r = table_add_many(table,
+ TABLE_FIELD, field,
+ TABLE_STRV_WRAPPED, l);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ return 0;
+}
+
+static int print_configuration(DNSConfiguration *configuration, StatusMode mode, bool *empty_line) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int r;
+
+ assert(configuration);
+
+ pager_open(arg_pager_flags);
+
+ bool global = !(configuration->ifindex > 0 || configuration->delegate);
+ if (mode == STATUS_DNS) {
+ _cleanup_strv_free_ char **l = NULL;
+ r = format_dns_servers(configuration, configuration->dns_servers, &l);
+ if (r < 0)
+ return r;
+
+ return status_print_strv(configuration, l);
+
+ } else if (mode == STATUS_DOMAIN) {
+ _cleanup_strv_free_ char **l = NULL;
+ r = format_search_domains(configuration, configuration->search_domains, &l);
+ if (r < 0)
return r;
return status_print_strv(configuration, l);
return status_full(STATUS_ALL, strv_skip(argv, 1));
}
-static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+static int verb_show_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ sd_json_variant *reply = NULL;
+ _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
int r;
(void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "i", arg_ifindex);
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
if (r < 0)
- return bus_log_create_error(r);
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
- r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
+ r = varlink_callbo_and_log(
+ vl,
+ "io.systemd.Resolve.Monitor.DumpStatistics",
+ &reply,
+ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
if (r < 0)
- return bus_log_create_error(r);
-
- /* If only argument is the empty string, then call SetLinkDNS() with an
- * empty list, which will clear the list of domains for an interface. */
- if (!strv_equal(dns, STRV_MAKE("")))
- STRV_FOREACH(p, dns) {
- _cleanup_free_ char *name = NULL;
- struct in_addr_data data;
- uint16_t port;
- int ifindex;
-
- r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
- if (r < 0)
- return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
-
- if (ifindex != 0 && ifindex != arg_ifindex)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
-
- r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "i", data.family);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
- if (r < 0)
- return bus_log_create_error(r);
+ return r;
- if (extended) {
- r = sd_bus_message_append(req, "q", port);
- if (r < 0)
- return bus_log_create_error(r);
+ if (sd_json_format_enabled(arg_json_format_flags))
+ return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
- r = sd_bus_message_append(req, "s", name);
- if (r < 0)
- return bus_log_create_error(r);
- }
+ struct statistics {
+ sd_json_variant *transactions;
+ sd_json_variant *cache;
+ sd_json_variant *dnssec;
+ } statistics;
- r = sd_bus_message_close_container(req);
- if (r < 0)
- return bus_log_create_error(r);
- }
+ static const sd_json_dispatch_field statistics_dispatch_table[] = {
+ { "transactions", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, transactions), SD_JSON_MANDATORY },
+ { "cache", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, cache), SD_JSON_MANDATORY },
+ { "dnssec", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct statistics, dnssec), SD_JSON_MANDATORY },
+ {},
+ };
- r = sd_bus_message_close_container(req);
+ r = sd_json_dispatch(reply, statistics_dispatch_table, SD_JSON_LOG, &statistics);
if (r < 0)
- return bus_log_create_error(r);
+ return r;
- r = sd_bus_call(bus, req, 0, error, NULL);
- if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
- sd_bus_error_free(error);
- return call_dns(bus, dns, locator, error, false);
- }
- return r;
-}
+ struct transactions {
+ uint64_t n_current_transactions;
+ uint64_t n_transactions_total;
+ uint64_t n_timeouts_total;
+ uint64_t n_timeouts_served_stale_total;
+ uint64_t n_failure_responses_total;
+ uint64_t n_failure_responses_served_stale_total;
+ } transactions;
-static int verb_dns(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
+ static const sd_json_dispatch_field transactions_dispatch_table[] = {
+ { "currentTransactions", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_current_transactions), SD_JSON_MANDATORY },
+ { "totalTransactions", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_transactions_total), SD_JSON_MANDATORY },
+ { "totalTimeouts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_timeouts_total), SD_JSON_MANDATORY },
+ { "totalTimeoutsServedStale", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_timeouts_served_stale_total), SD_JSON_MANDATORY },
+ { "totalFailedResponses", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_failure_responses_total), SD_JSON_MANDATORY },
+ { "totalFailedResponsesServedStale", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct transactions, n_failure_responses_served_stale_total), SD_JSON_MANDATORY },
+ {},
+ };
- r = acquire_bus(&bus);
+ r = sd_json_dispatch(statistics.transactions, transactions_dispatch_table, SD_JSON_LOG, &transactions);
if (r < 0)
return r;
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_DNS);
+ struct cache {
+ uint64_t cache_size;
+ uint64_t n_cache_hit;
+ uint64_t n_cache_miss;
+ } cache;
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_DNS);
+ static const sd_json_dispatch_field cache_dispatch_table[] = {
+ { "size", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, cache_size), SD_JSON_MANDATORY },
+ { "hits", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, n_cache_hit), SD_JSON_MANDATORY },
+ { "misses", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct cache, n_cache_miss), SD_JSON_MANDATORY },
+ {},
+ };
- char **args = strv_skip(argv, 2);
- r = call_dns(bus, args, bus_resolve_mgr, &error, true);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ r = sd_json_dispatch(statistics.cache, cache_dispatch_table, SD_JSON_LOG, &cache);
+ if (r < 0)
+ return r;
- r = call_dns(bus, args, bus_network_mgr, &error, true);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ struct dnsssec {
+ uint64_t n_dnssec_secure;
+ uint64_t n_dnssec_insecure;
+ uint64_t n_dnssec_bogus;
+ uint64_t n_dnssec_indeterminate;
+ } dnsssec;
- return log_error_errno(r, "Failed to set DNS configuration: %s", bus_error_message(&error, r));
- }
+ static const sd_json_dispatch_field dnssec_dispatch_table[] = {
+ { "secure", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_secure), SD_JSON_MANDATORY },
+ { "insecure", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_insecure), SD_JSON_MANDATORY },
+ { "bogus", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_bogus), SD_JSON_MANDATORY },
+ { "indeterminate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct dnsssec, n_dnssec_indeterminate), SD_JSON_MANDATORY },
+ {},
+ };
- return 0;
-}
+ r = sd_json_dispatch(statistics.dnssec, dnssec_dispatch_table, SD_JSON_LOG, &dnsssec);
+ if (r < 0)
+ return r;
-static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
- int r;
+ table = table_new_vertical();
+ if (!table)
+ return log_oom();
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
-
- r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(req, "i", arg_ifindex);
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_open_container(req, 'a', "(sb)");
+ r = table_add_many(table,
+ TABLE_STRING, "Transactions",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_FIELD, "Current Transactions",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, transactions.n_current_transactions,
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_FIELD, "Total Transactions",
+ TABLE_UINT64, transactions.n_transactions_total,
+ TABLE_EMPTY, TABLE_EMPTY,
+ TABLE_STRING, "Cache",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_FIELD, "Current Cache Size",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, cache.cache_size,
+ TABLE_FIELD, "Cache Hits",
+ TABLE_UINT64, cache.n_cache_hit,
+ TABLE_FIELD, "Cache Misses",
+ TABLE_UINT64, cache.n_cache_miss,
+ TABLE_EMPTY, TABLE_EMPTY,
+ TABLE_STRING, "Failure Transactions",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_FIELD, "Total Timeouts",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, transactions.n_timeouts_total,
+ TABLE_FIELD, "Total Timeouts (Stale Data Served)",
+ TABLE_UINT64, transactions.n_timeouts_served_stale_total,
+ TABLE_FIELD, "Total Failure Responses",
+ TABLE_UINT64, transactions.n_failure_responses_total,
+ TABLE_FIELD, "Total Failure Responses (Stale Data Served)",
+ TABLE_UINT64, transactions.n_failure_responses_served_stale_total,
+ TABLE_EMPTY, TABLE_EMPTY,
+ TABLE_STRING, "DNSSEC Verdicts",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_FIELD, "Secure",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, dnsssec.n_dnssec_secure,
+ TABLE_FIELD, "Insecure",
+ TABLE_UINT64, dnsssec.n_dnssec_insecure,
+ TABLE_FIELD, "Bogus",
+ TABLE_UINT64, dnsssec.n_dnssec_bogus,
+ TABLE_FIELD, "Indeterminate",
+ TABLE_UINT64, dnsssec.n_dnssec_indeterminate
+ );
if (r < 0)
- return bus_log_create_error(r);
+ return table_log_add_error(r);
- /* If only argument is the empty string, then call SetLinkDomains() with an
- * empty list, which will clear the list of domains for an interface. */
- if (!strv_equal(domain, STRV_MAKE("")))
- STRV_FOREACH(p, domain) {
- const char *n;
+ return table_print_or_warn(table);
+}
- n = **p == '~' ? *p + 1 : *p;
+static int verb_reset_statistics(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ sd_json_variant *reply = NULL;
+ _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+ int r;
- r = dns_name_is_valid(n);
- if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Domain not valid: %s",
- n);
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- r = sd_bus_message_append(req, "(sb)", n, **p == '~');
- if (r < 0)
- return bus_log_create_error(r);
- }
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
- r = sd_bus_message_close_container(req);
+ r = varlink_callbo_and_log(
+ vl,
+ "io.systemd.Resolve.Monitor.ResetStatistics",
+ &reply,
+ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
if (r < 0)
- return bus_log_create_error(r);
+ return r;
- return sd_bus_call(bus, req, 0, error, NULL);
+ if (sd_json_format_enabled(arg_json_format_flags))
+ return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
+
+ return 0;
}
-static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) {
+static int verb_flush_caches(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
if (r < 0)
return r;
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_DOMAIN);
-
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_DOMAIN);
-
- char **args = strv_skip(argv, 2);
- r = call_domain(bus, args, bus_resolve_mgr, &error);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
-
- r = call_domain(bus, args, bus_network_mgr, &error);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
-
- return log_error_errno(r, "Failed to set domain configuration: %s", bus_error_message(&error, r));
- }
+ r = bus_call_method(bus, bus_resolve_mgr, "FlushCaches", &error, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r));
return 0;
}
-static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *userdata) {
+static int verb_reset_server_features(int argc, char *argv[], uintptr_t _data, void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r, b;
+ int r;
r = acquire_bus(&bus);
if (r < 0)
return r;
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_DEFAULT_ROUTE);
+ r = bus_call_method(bus, bus_resolve_mgr, "ResetServerFeatures", &error, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r));
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_DEFAULT_ROUTE);
+ return 0;
+}
- b = parse_boolean(argv[2]);
- if (b < 0)
- return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
+static int print_question(char prefix, const char *color, sd_json_variant *question) {
+ sd_json_variant *q = NULL;
+ int r;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ assert(color);
- r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ JSON_VARIANT_ARRAY_FOREACH(q, question) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ char buf[DNS_RESOURCE_KEY_STRING_MAX];
- r = bus_call_method(bus, bus_network_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ r = dns_resource_key_from_json(q, &key);
+ if (r < 0) {
+ log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
+ continue;
+ }
- return log_error_errno(r, "Failed to set default route configuration: %s", bus_error_message(&error, r));
+ printf("%s%s %c%s: %s\n",
+ color,
+ glyph(GLYPH_ARROW_RIGHT),
+ prefix,
+ ansi_normal(),
+ dns_resource_key_to_string(key, buf, sizeof(buf)));
}
return 0;
}
-static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *global_llmnr_support_str = NULL;
- ResolveSupport global_llmnr_support, llmnr_support;
+static int print_answer(sd_json_variant *answer) {
+ sd_json_variant *a;
int r;
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
-
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_LLMNR);
-
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_LLMNR);
+ JSON_VARIANT_ARRAY_FOREACH(a, answer) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_free_ void *d = NULL;
+ sd_json_variant *jraw;
+ const char *s;
+ size_t l;
- llmnr_support = resolve_support_from_string(argv[2]);
- if (llmnr_support < 0)
- return log_error_errno(llmnr_support, "Invalid LLMNR setting: %s", argv[2]);
+ jraw = sd_json_variant_by_key(a, "raw");
+ if (!jraw) {
+ log_warning("Received monitor answer lacking valid raw data, ignoring.");
+ continue;
+ }
- r = bus_get_property_string(bus, bus_resolve_mgr, "LLMNR", &error, &global_llmnr_support_str);
- if (r < 0)
- return log_error_errno(r, "Failed to get the global LLMNR support state: %s", bus_error_message(&error, r));
+ r = sd_json_variant_unbase64(jraw, &d, &l);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to undo base64 encoding of monitor answer raw data, ignoring.");
+ continue;
+ }
- global_llmnr_support = resolve_support_from_string(global_llmnr_support_str);
- if (global_llmnr_support < 0)
- return log_error_errno(global_llmnr_support, "Received invalid global LLMNR setting: %s", global_llmnr_support_str);
+ r = dns_resource_record_new_from_raw(&rr, d, l);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse monitor answer RR, ignoring: %m");
+ continue;
+ }
- if (global_llmnr_support < llmnr_support)
- log_warning("Setting LLMNR support level \"%s\" for \"%s\", but the global support level is \"%s\".",
- argv[2], arg_ifname, global_llmnr_support_str);
+ s = dns_resource_record_to_string(rr);
+ if (!s)
+ return log_oom();
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ printf("%s%s A%s: %s\n",
+ ansi_highlight_yellow(),
+ glyph(GLYPH_ARROW_LEFT),
+ ansi_normal(),
+ s);
+ }
- r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ return 0;
+}
- r = bus_call_method(bus, bus_network_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+typedef struct MonitorQueryParams {
+ sd_json_variant *question;
+ sd_json_variant *answer;
+ sd_json_variant *collected_questions;
+ int rcode;
+ int error;
+ int ede_code;
+ const char *state;
+ const char *result;
+ const char *ede_msg;
+} MonitorQueryParams;
- return log_error_errno(r, "Failed to set LLMNR configuration: %s", bus_error_message(&error, r));
- }
+static void monitor_query_params_done(MonitorQueryParams *p) {
+ assert(p);
- return 0;
+ sd_json_variant_unref(p->question);
+ sd_json_variant_unref(p->answer);
+ sd_json_variant_unref(p->collected_questions);
}
-static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *global_mdns_support_str = NULL;
- ResolveSupport global_mdns_support, mdns_support;
- int r;
-
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
-
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_MDNS);
+static void monitor_query_dump(sd_json_variant *v) {
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, question), SD_JSON_MANDATORY },
+ { "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, answer), 0 },
+ { "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, collected_questions), 0 },
+ { "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, state), SD_JSON_MANDATORY },
+ { "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, result), 0 },
+ { "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, rcode), 0 },
+ { "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, error), 0 },
+ { "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, ede_code), 0 },
+ { "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, ede_msg), 0 },
+ {}
+ };
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_MDNS);
+ _cleanup_(monitor_query_params_done) MonitorQueryParams p = {
+ .rcode = -1,
+ .ede_code = -1,
+ };
- mdns_support = resolve_support_from_string(argv[2]);
- if (mdns_support < 0)
- return log_error_errno(mdns_support, "Invalid mDNS setting: %s", argv[2]);
+ assert(v);
- r = bus_get_property_string(bus, bus_resolve_mgr, "MulticastDNS", &error, &global_mdns_support_str);
- if (r < 0)
- return log_error_errno(r, "Failed to get the global mDNS support state: %s", bus_error_message(&error, r));
+ if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &p) < 0)
+ return;
- global_mdns_support = resolve_support_from_string(global_mdns_support_str);
- if (global_mdns_support < 0)
- return log_error_errno(global_mdns_support, "Received invalid global mDNS setting: %s", global_mdns_support_str);
+ /* First show the current question */
+ print_question('Q', ansi_highlight_cyan(), p.question);
- if (global_mdns_support < mdns_support)
- log_warning("Setting mDNS support level \"%s\" for \"%s\", but the global support level is \"%s\".",
- argv[2], arg_ifname, global_mdns_support_str);
+ /* And then show the questions that led to this one in case this was a CNAME chain */
+ print_question('C', ansi_highlight_grey(), p.collected_questions);
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ printf("%s%s S%s: %s",
+ streq_ptr(p.state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
+ glyph(GLYPH_ARROW_LEFT),
+ ansi_normal(),
+ streq_ptr(p.state, "errno") ? ERRNO_NAME(p.error) :
+ streq_ptr(p.state, "rcode-failure") ? strna(dns_rcode_to_string(p.rcode)) :
+ strna(p.state));
- r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ if (!isempty(p.result))
+ printf(": %s", p.result);
- r = bus_call_method(
- bus,
- bus_network_mgr,
- "SetLinkMulticastDNS",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ if (p.ede_code >= 0)
+ printf(" (%s%s%s)",
+ FORMAT_DNS_EDE_RCODE(p.ede_code),
+ !isempty(p.ede_msg) ? ": " : "",
+ strempty(p.ede_msg));
- return log_error_errno(r, "Failed to set MulticastDNS configuration: %s", bus_error_message(&error, r));
- }
+ puts("");
- return 0;
+ print_answer(p.answer);
}
-static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
-
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
-
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
-
- if (arg_ifindex <= 0)
- return status_all(STATUS_DNS_OVER_TLS);
+static int monitor_reply(
+ sd_varlink *link,
+ sd_json_variant *parameters,
+ const char *error_id,
+ sd_varlink_reply_flags_t flags,
+ void *userdata) {
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_DNS_OVER_TLS);
+ assert(link);
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ if (error_id) {
+ bool disconnect;
- r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSOverTLS", &error, NULL, "is", arg_ifindex, argv[2]);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ disconnect = streq(error_id, SD_VARLINK_ERROR_DISCONNECTED);
+ if (disconnect)
+ log_info("Disconnected.");
+ else
+ log_error("Varlink error: %s", error_id);
- r = bus_call_method(
- bus,
- bus_network_mgr,
- "SetLinkDNSOverTLS",
- &error,
- NULL,
- "is", arg_ifindex, argv[2]);
+ (void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
+ return 0;
}
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
- return log_error_errno(r, "Failed to set DNSOverTLS configuration: %s", bus_error_message(&error, r));
+ if (sd_json_variant_by_key(parameters, "ready")) {
+ /* The first message coming in will just indicate that we are now subscribed. We let our
+ * caller know if they asked for it. Once the caller sees this they should know that we are
+ * not going to miss any queries anymore. */
+ (void) sd_notify(/* unset_environment=false */ false, "READY=1");
+ return 0;
}
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
+ monitor_query_dump(parameters);
+ printf("\n");
+ } else
+ sd_json_variant_dump(parameters, arg_json_format_flags, NULL, NULL);
+
+ fflush(stdout);
+
return 0;
}
-static int verb_dnssec(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
+static int verb_monitor(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+ int r, c;
- r = acquire_bus(&bus);
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ r = sd_event_default(&event);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to get event loop: %m");
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
+ r = sd_event_set_signal_exit(event, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
- if (arg_ifindex <= 0)
- return status_all(STATUS_DNSSEC);
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_DNSSEC);
+ r = sd_varlink_set_relative_timeout(vl, USEC_INFINITY); /* We want the monitor to run basically forever */
+ if (r < 0)
+ return log_error_errno(r, "Failed to set varlink timeout: %m");
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ r = sd_varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
- r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ r = sd_varlink_bind_reply(vl, monitor_reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind reply callback to varlink connection: %m");
- r = bus_call_method(bus, bus_network_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ r = sd_varlink_observebo(
+ vl,
+ "io.systemd.Resolve.Monitor.SubscribeQueryResults",
+ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue SubscribeQueryResults() varlink call: %m");
- return log_error_errno(r, "Failed to set DNSSEC configuration: %s", bus_error_message(&error, r));
- }
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
- return 0;
+ r = sd_event_get_exit_code(event, &c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get exit code: %m");
+
+ return c;
}
-static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_error *error) {
+static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
int r;
(void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNSSECNegativeTrustAnchors");
+ r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_strv(req, nta);
+ r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
if (r < 0)
return bus_log_create_error(r);
- return sd_bus_call(bus, req, 0, error, NULL);
-}
-
-static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- char **args;
- bool clear;
- int r;
+ /* If only argument is the empty string, then call SetLinkDNS() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(dns, STRV_MAKE("")))
+ STRV_FOREACH(p, dns) {
+ _cleanup_free_ char *name = NULL;
+ struct in_addr_data data;
+ uint16_t port;
+ int ifindex;
- r = acquire_bus(&bus);
- if (r < 0)
- return r;
+ r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
+ if (ifindex != 0 && ifindex != arg_ifindex)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
- if (arg_ifindex <= 0)
- return status_all(STATUS_NTA);
+ r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
+ if (r < 0)
+ return bus_log_create_error(r);
- if (argc < 3)
- return status_ifindex(arg_ifindex, STATUS_NTA);
+ r = sd_bus_message_append(req, "i", data.family);
+ if (r < 0)
+ return bus_log_create_error(r);
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ r = sd_bus_message_append_array(req, 'y', &data.address, FAMILY_ADDRESS_SIZE(data.family));
+ if (r < 0)
+ return bus_log_create_error(r);
- /* If only argument is the empty string, then call SetLinkDNSSECNegativeTrustAnchors()
- * with an empty list, which will clear the list of domains for an interface. */
- args = strv_skip(argv, 2);
- clear = strv_equal(args, STRV_MAKE(""));
+ if (extended) {
+ r = sd_bus_message_append(req, "q", port);
+ if (r < 0)
+ return bus_log_create_error(r);
- if (!clear)
- STRV_FOREACH(p, args) {
- r = dns_name_is_valid(*p);
+ r = sd_bus_message_append(req, "s", name);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(req);
if (r < 0)
- return log_error_errno(r, "Failed to validate specified domain %s: %m", *p);
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Domain not valid: %s",
- *p);
+ return bus_log_create_error(r);
}
- r = call_nta(bus, clear ? NULL : args, bus_resolve_mgr, &error);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ r = sd_bus_message_close_container(req);
+ if (r < 0)
+ return bus_log_create_error(r);
- r = call_nta(bus, clear ? NULL : args, bus_network_mgr, &error);
+ r = sd_bus_call(bus, req, 0, error, NULL);
+ if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ sd_bus_error_free(error);
+ return call_dns(bus, dns, locator, error, false);
}
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ return r;
+}
- return log_error_errno(r, "Failed to set DNSSEC NTA configuration: %s", bus_error_message(&error, r));
- }
+static int dump_cache_item(sd_json_variant *item) {
- return 0;
-}
+ struct item_info {
+ sd_json_variant *key;
+ sd_json_variant *rrs;
+ const char *type;
+ uint64_t until;
+ } item_info = {};
-static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- int r;
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "key", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct item_info, key), SD_JSON_MANDATORY },
+ { "rrs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(struct item_info, rrs), 0 },
+ { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct item_info, type), 0 },
+ { "until", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct item_info, until), 0 },
+ {},
+ };
- r = acquire_bus(&bus);
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
+ int r, c = 0;
+
+ r = sd_json_dispatch(item, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &item_info);
if (r < 0)
return r;
- if (argc >= 2) {
- r = ifname_mangle(argv[1]);
- if (r < 0)
- return r;
- }
+ r = dns_resource_key_from_json(item_info.key, &k);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn JSON data to resource key: %m");
- if (arg_ifindex <= 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
+ if (item_info.type)
+ printf("%s %s%s%s\n", DNS_RESOURCE_KEY_TO_STRING(k), ansi_highlight_red(), item_info.type, ansi_normal());
+ else {
+ sd_json_variant *i;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+ JSON_VARIANT_ARRAY_FOREACH(i, item_info.rrs) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ _cleanup_free_ void *data = NULL;
+ sd_json_variant *raw;
+ size_t size;
- r = bus_call_method(bus, bus_resolve_mgr, "RevertLink", &error, NULL, "i", arg_ifindex);
- if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
- sd_bus_error_free(&error);
+ raw = sd_json_variant_by_key(i, "raw");
+ if (!raw)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "raw field missing from RR JSON data.");
- r = bus_call_method(bus, bus_network_mgr, "RevertLinkDNS", &error, NULL, "i", arg_ifindex);
- }
- if (r < 0) {
- if (arg_ifindex_permissive &&
- sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
- return 0;
+ r = sd_json_variant_unbase64(raw, &data, &size);
+ if (r < 0)
+ return log_error_errno(r, "Unable to decode raw RR JSON data: %m");
- return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
+ r = dns_resource_record_new_from_raw(&rr, data, size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse DNS data: %m");
+
+ printf("%s\n", dns_resource_record_to_string(rr));
+ c++;
+ }
}
- return 0;
+ return c;
}
-static int verb_log_level(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- int r;
+static int dump_cache_scope(sd_json_variant *scope) {
+ struct scope_info {
+ const char *protocol;
+ int family;
+ int ifindex;
+ const char *ifname;
+ sd_json_variant *cache;
+ const char *dnssec_mode;
+ const char *dns_over_tls_mode;
+ } scope_info = {
+ .family = AF_UNSPEC,
+ };
+ sd_json_variant *i;
+ int r, c = 0;
- r = acquire_bus(&bus);
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, protocol), SD_JSON_MANDATORY },
+ { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(struct scope_info, family), 0 },
+ { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(struct scope_info, ifindex), SD_JSON_RELAX },
+ { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, ifname), 0 },
+ { "cache", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(struct scope_info, cache), SD_JSON_MANDATORY },
+ { "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, dnssec_mode), 0 },
+ { "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, dns_over_tls_mode), 0 },
+ {},
+ };
+
+ r = sd_json_dispatch(scope, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &scope_info);
if (r < 0)
return r;
- assert(IN_SET(argc, 1, 2));
+ printf("%sScope protocol=%s", ansi_underline(), scope_info.protocol);
- return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
-}
+ if (scope_info.family != AF_UNSPEC)
+ printf(" family=%s", af_to_name(scope_info.family));
-static int print_question(char prefix, const char *color, sd_json_variant *question) {
- sd_json_variant *q = NULL;
- int r;
+ if (scope_info.ifindex > 0)
+ printf(" ifindex=%i", scope_info.ifindex);
+ if (scope_info.ifname)
+ printf(" ifname=%s", scope_info.ifname);
- assert(color);
+ if (dns_protocol_from_string(scope_info.protocol) == DNS_PROTOCOL_DNS) {
+ if (scope_info.dnssec_mode)
+ printf(" DNSSEC=%s", scope_info.dnssec_mode);
+ if (scope_info.dns_over_tls_mode)
+ printf(" DNSOverTLS=%s", scope_info.dns_over_tls_mode);
+ }
- JSON_VARIANT_ARRAY_FOREACH(q, question) {
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- char buf[DNS_RESOURCE_KEY_STRING_MAX];
+ printf("%s\n", ansi_normal());
- r = dns_resource_key_from_json(q, &key);
- if (r < 0) {
- log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
- continue;
- }
+ JSON_VARIANT_ARRAY_FOREACH(i, scope_info.cache) {
+ r = dump_cache_item(i);
+ if (r < 0)
+ return r;
- printf("%s%s %c%s: %s\n",
- color,
- glyph(GLYPH_ARROW_RIGHT),
- prefix,
- ansi_normal(),
- dns_resource_key_to_string(key, buf, sizeof(buf)));
+ c += r;
}
+ if (c == 0)
+ printf("%sNo entries.%s\n\n", ansi_grey(), ansi_normal());
+ else
+ printf("\n");
+
return 0;
}
-static int print_answer(sd_json_variant *answer) {
- sd_json_variant *a;
+static int verb_show_cache(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ sd_json_variant *reply = NULL, *d = NULL;
+ _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
int r;
- JSON_VARIANT_ARRAY_FOREACH(a, answer) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_free_ void *d = NULL;
- sd_json_variant *jraw;
- const char *s;
- size_t l;
-
- jraw = sd_json_variant_by_key(a, "raw");
- if (!jraw) {
- log_warning("Received monitor answer lacking valid raw data, ignoring.");
- continue;
- }
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- r = sd_json_variant_unbase64(jraw, &d, &l);
- if (r < 0) {
- log_warning_errno(r, "Failed to undo base64 encoding of monitor answer raw data, ignoring.");
- continue;
- }
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
- r = dns_resource_record_new_from_raw(&rr, d, l);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse monitor answer RR, ignoring: %m");
- continue;
- }
+ r = varlink_callbo_and_log(
+ vl,
+ "io.systemd.Resolve.Monitor.DumpCache",
+ &reply,
+ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ if (r < 0)
+ return r;
- s = dns_resource_record_to_string(rr);
- if (!s)
- return log_oom();
+ d = sd_json_variant_by_key(reply, "dump");
+ if (!d)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response is missing 'dump' key.");
- printf("%s%s A%s: %s\n",
- ansi_highlight_yellow(),
- glyph(GLYPH_ARROW_LEFT),
- ansi_normal(),
- s);
+ if (!sd_json_variant_is_array(d))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response 'dump' field not an array");
+
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
+ sd_json_variant *i;
+
+ JSON_VARIANT_ARRAY_FOREACH(i, d) {
+ r = dump_cache_scope(i);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
- return 0;
+ return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL);
}
-typedef struct MonitorQueryParams {
- sd_json_variant *question;
- sd_json_variant *answer;
- sd_json_variant *collected_questions;
- int rcode;
- int error;
- int ede_code;
- const char *state;
- const char *result;
- const char *ede_msg;
-} MonitorQueryParams;
+static int dump_server_state(sd_json_variant *server) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ TableCell *cell;
-static void monitor_query_params_done(MonitorQueryParams *p) {
- assert(p);
+ struct server_state {
+ const char *server_name;
+ const char *type;
+ const char *ifname;
+ int ifindex;
+ const char *verified_feature_level;
+ const char *possible_feature_level;
+ const char *dnssec_mode;
+ bool dnssec_supported;
+ size_t received_udp_fragment_max;
+ uint64_t n_failed_udp;
+ uint64_t n_failed_tcp;
+ bool packet_truncated;
+ bool packet_bad_opt;
+ bool packet_rrsig_missing;
+ bool packet_invalid;
+ bool packet_do_off;
+ } server_state = {
+ .ifindex = -1,
+ };
- sd_json_variant_unref(p->question);
- sd_json_variant_unref(p->answer);
- sd_json_variant_unref(p->collected_questions);
-}
+ int r;
-static void monitor_query_dump(sd_json_variant *v) {
static const sd_json_dispatch_field dispatch_table[] = {
- { "question", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, question), SD_JSON_MANDATORY },
- { "answer", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, answer), 0 },
- { "collectedQuestions", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(MonitorQueryParams, collected_questions), 0 },
- { "state", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, state), SD_JSON_MANDATORY },
- { "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, result), 0 },
- { "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, rcode), 0 },
- { "errno", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, error), 0 },
- { "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(MonitorQueryParams, ede_code), 0 },
- { "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MonitorQueryParams, ede_msg), 0 },
- {}
+ { "Server", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, server_name), SD_JSON_MANDATORY },
+ { "Type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, type), SD_JSON_MANDATORY },
+ { "Interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, ifname), 0 },
+ { "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(struct server_state, ifindex), SD_JSON_RELAX },
+ { "VerifiedFeatureLevel", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, verified_feature_level), 0 },
+ { "PossibleFeatureLevel", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, possible_feature_level), 0 },
+ { "DNSSECMode", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, dnssec_mode), SD_JSON_MANDATORY },
+ { "DNSSECSupported", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, dnssec_supported), SD_JSON_MANDATORY },
+ { "ReceivedUDPFragmentMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, received_udp_fragment_max), SD_JSON_MANDATORY },
+ { "FailedUDPAttempts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, n_failed_udp), SD_JSON_MANDATORY },
+ { "FailedTCPAttempts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, n_failed_tcp), SD_JSON_MANDATORY },
+ { "PacketTruncated", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_truncated), SD_JSON_MANDATORY },
+ { "PacketBadOpt", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_bad_opt), SD_JSON_MANDATORY },
+ { "PacketRRSIGMissing", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_rrsig_missing), SD_JSON_MANDATORY },
+ { "PacketInvalid", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_invalid), SD_JSON_MANDATORY },
+ { "PacketDoOff", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_do_off), SD_JSON_MANDATORY },
+ {},
};
- _cleanup_(monitor_query_params_done) MonitorQueryParams p = {
- .rcode = -1,
- .ede_code = -1,
- };
+ r = sd_json_dispatch(server, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &server_state);
+ if (r < 0)
+ return r;
- assert(v);
+ table = table_new_vertical();
+ if (!table)
+ return log_oom();
- if (sd_json_dispatch(v, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &p) < 0)
- return;
+ assert_se(cell = table_get_cell(table, 0, 0));
+ (void) table_set_ellipsize_percent(table, cell, 100);
+ (void) table_set_align_percent(table, cell, 0);
- /* First show the current question */
- print_question('Q', ansi_highlight_cyan(), p.question);
+ r = table_add_cell_stringf(table, NULL, "Server: %s", server_state.server_name);
+ if (r < 0)
+ return table_log_add_error(r);
- /* And then show the questions that led to this one in case this was a CNAME chain */
- print_question('C', ansi_highlight_grey(), p.collected_questions);
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_FIELD, "Type",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_STRING, server_state.type);
+ if (r < 0)
+ return table_log_add_error(r);
- printf("%s%s S%s: %s",
- streq_ptr(p.state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
- glyph(GLYPH_ARROW_LEFT),
- ansi_normal(),
- streq_ptr(p.state, "errno") ? ERRNO_NAME(p.error) :
- streq_ptr(p.state, "rcode-failure") ? strna(dns_rcode_to_string(p.rcode)) :
- strna(p.state));
+ if (server_state.ifname) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Interface",
+ TABLE_STRING, server_state.ifname);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (!isempty(p.result))
- printf(": %s", p.result);
+ if (server_state.ifindex >= 0) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Interface Index",
+ TABLE_INT, server_state.ifindex);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (p.ede_code >= 0)
- printf(" (%s%s%s)",
- FORMAT_DNS_EDE_RCODE(p.ede_code),
- !isempty(p.ede_msg) ? ": " : "",
- strempty(p.ede_msg));
+ if (server_state.verified_feature_level) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Verified feature level",
+ TABLE_STRING, server_state.verified_feature_level);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- puts("");
+ if (server_state.possible_feature_level) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Possible feature level",
+ TABLE_STRING, server_state.possible_feature_level);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- print_answer(p.answer);
+ r = table_add_many(table,
+ TABLE_FIELD, "DNSSEC Mode",
+ TABLE_STRING, server_state.dnssec_mode,
+ TABLE_FIELD, "DNSSEC Supported",
+ TABLE_STRING, yes_no(server_state.dnssec_supported),
+ TABLE_FIELD, "Maximum UDP fragment size received",
+ TABLE_UINT64, server_state.received_udp_fragment_max,
+ TABLE_FIELD, "Failed UDP attempts",
+ TABLE_UINT64, server_state.n_failed_udp,
+ TABLE_FIELD, "Failed TCP attempts",
+ TABLE_UINT64, server_state.n_failed_tcp,
+ TABLE_FIELD, "Seen truncated packet",
+ TABLE_STRING, yes_no(server_state.packet_truncated),
+ TABLE_FIELD, "Seen OPT RR getting lost",
+ TABLE_STRING, yes_no(server_state.packet_bad_opt),
+ TABLE_FIELD, "Seen RRSIG RR missing",
+ TABLE_STRING, yes_no(server_state.packet_rrsig_missing),
+ TABLE_FIELD, "Seen invalid packet",
+ TABLE_STRING, yes_no(server_state.packet_invalid),
+ TABLE_FIELD, "Server dropped DO flag",
+ TABLE_STRING, yes_no(server_state.packet_do_off),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY, TABLE_EMPTY);
+
+ if (r < 0)
+ return table_log_add_error(r);
+
+ return table_print_or_warn(table);
}
-static int monitor_reply(
- sd_varlink *link,
- sd_json_variant *parameters,
- const char *error_id,
- sd_varlink_reply_flags_t flags,
- void *userdata) {
+static int verb_show_server_state(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ sd_json_variant *reply = NULL, *d = NULL;
+ _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+ int r;
- assert(link);
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- if (error_id) {
- bool disconnect;
+ r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
- disconnect = streq(error_id, SD_VARLINK_ERROR_DISCONNECTED);
- if (disconnect)
- log_info("Disconnected.");
- else
- log_error("Varlink error: %s", error_id);
+ r = varlink_callbo_and_log(
+ vl,
+ "io.systemd.Resolve.Monitor.DumpServerState",
+ &reply,
+ SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ if (r < 0)
+ return r;
+
+ d = sd_json_variant_by_key(reply, "dump");
+ if (!d)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response is missing 'dump' key.");
+
+ if (!sd_json_variant_is_array(d))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "DumpCache() response 'dump' field not an array");
+
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
+ sd_json_variant *i;
+
+ JSON_VARIANT_ARRAY_FOREACH(i, d) {
+ r = dump_server_state(i);
+ if (r < 0)
+ return r;
+ }
- (void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
return 0;
}
- if (sd_json_variant_by_key(parameters, "ready")) {
- /* The first message coming in will just indicate that we are now subscribed. We let our
- * caller know if they asked for it. Once the caller sees this they should know that we are
- * not going to miss any queries anymore. */
- (void) sd_notify(/* unset_environment=false */ false, "READY=1");
- return 0;
+ return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL);
+}
+
+static int verb_dns(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
}
- if (!sd_json_format_enabled(arg_json_format_flags)) {
- monitor_query_dump(parameters);
- printf("\n");
- } else
- sd_json_variant_dump(parameters, arg_json_format_flags, NULL, NULL);
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_DNS);
+
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_DNS);
+
+ char **args = strv_skip(argv, 2);
+ r = call_dns(bus, args, bus_resolve_mgr, &error, true);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = call_dns(bus, args, bus_network_mgr, &error, true);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
+
+ return log_error_errno(r, "Failed to set DNS configuration: %s", bus_error_message(&error, r));
+ }
+
+ return 0;
+}
+
+static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+ int r;
+
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "i", arg_ifindex);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(req, 'a', "(sb)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* If only argument is the empty string, then call SetLinkDomains() with an
+ * empty list, which will clear the list of domains for an interface. */
+ if (!strv_equal(domain, STRV_MAKE("")))
+ STRV_FOREACH(p, domain) {
+ const char *n;
+
+ n = **p == '~' ? *p + 1 : *p;
+
+ r = dns_name_is_valid(n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", n);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Domain not valid: %s",
+ n);
+
+ r = sd_bus_message_append(req, "(sb)", n, **p == '~');
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(req);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return sd_bus_call(bus, req, 0, error, NULL);
+}
+
+static int verb_domain(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_DOMAIN);
+
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_DOMAIN);
+
+ char **args = strv_skip(argv, 2);
+ r = call_domain(bus, args, bus_resolve_mgr, &error);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = call_domain(bus, args, bus_network_mgr, &error);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
+
+ return log_error_errno(r, "Failed to set domain configuration: %s", bus_error_message(&error, r));
+ }
+
+ return 0;
+}
+
+static int verb_default_route(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r, b;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_DEFAULT_ROUTE);
+
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_DEFAULT_ROUTE);
+
+ b = parse_boolean(argv[2]);
+ if (b < 0)
+ return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
+
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = bus_call_method(bus, bus_network_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
+
+ return log_error_errno(r, "Failed to set default route configuration: %s", bus_error_message(&error, r));
+ }
+
+ return 0;
+}
+
+static int verb_llmnr(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *global_llmnr_support_str = NULL;
+ ResolveSupport global_llmnr_support, llmnr_support;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_LLMNR);
+
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_LLMNR);
+
+ llmnr_support = resolve_support_from_string(argv[2]);
+ if (llmnr_support < 0)
+ return log_error_errno(llmnr_support, "Invalid LLMNR setting: %s", argv[2]);
+
+ r = bus_get_property_string(bus, bus_resolve_mgr, "LLMNR", &error, &global_llmnr_support_str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the global LLMNR support state: %s", bus_error_message(&error, r));
+
+ global_llmnr_support = resolve_support_from_string(global_llmnr_support_str);
+ if (global_llmnr_support < 0)
+ return log_error_errno(global_llmnr_support, "Received invalid global LLMNR setting: %s", global_llmnr_support_str);
+
+ if (global_llmnr_support < llmnr_support)
+ log_warning("Setting LLMNR support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+ argv[2], arg_ifname, global_llmnr_support_str);
+
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
+
+ r = bus_call_method(bus, bus_network_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- fflush(stdout);
+ return log_error_errno(r, "Failed to set LLMNR configuration: %s", bus_error_message(&error, r));
+ }
return 0;
}
-static int verb_monitor(int argc, char *argv[], uintptr_t _data, void *userdata) {
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
- int r, c;
-
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+static int verb_mdns(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *global_mdns_support_str = NULL;
+ ResolveSupport global_mdns_support, mdns_support;
+ int r;
- r = sd_event_default(&event);
+ r = acquire_bus(&bus);
if (r < 0)
- return log_error_errno(r, "Failed to get event loop: %m");
+ return r;
- r = sd_event_set_signal_exit(event, true);
- if (r < 0)
- return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
- if (r < 0)
- return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_MDNS);
- r = sd_varlink_set_relative_timeout(vl, USEC_INFINITY); /* We want the monitor to run basically forever */
- if (r < 0)
- return log_error_errno(r, "Failed to set varlink timeout: %m");
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_MDNS);
- r = sd_varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL);
- if (r < 0)
- return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+ mdns_support = resolve_support_from_string(argv[2]);
+ if (mdns_support < 0)
+ return log_error_errno(mdns_support, "Invalid mDNS setting: %s", argv[2]);
- r = sd_varlink_bind_reply(vl, monitor_reply);
+ r = bus_get_property_string(bus, bus_resolve_mgr, "MulticastDNS", &error, &global_mdns_support_str);
if (r < 0)
- return log_error_errno(r, "Failed to bind reply callback to varlink connection: %m");
+ return log_error_errno(r, "Failed to get the global mDNS support state: %s", bus_error_message(&error, r));
- r = sd_varlink_observebo(
- vl,
- "io.systemd.Resolve.Monitor.SubscribeQueryResults",
- SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
- if (r < 0)
- return log_error_errno(r, "Failed to issue SubscribeQueryResults() varlink call: %m");
+ global_mdns_support = resolve_support_from_string(global_mdns_support_str);
+ if (global_mdns_support < 0)
+ return log_error_errno(global_mdns_support, "Received invalid global mDNS setting: %s", global_mdns_support_str);
- r = sd_event_loop(event);
- if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
+ if (global_mdns_support < mdns_support)
+ log_warning("Setting mDNS support level \"%s\" for \"%s\", but the global support level is \"%s\".",
+ argv[2], arg_ifname, global_mdns_support_str);
- r = sd_event_get_exit_code(event, &c);
- if (r < 0)
- return log_error_errno(r, "Failed to get exit code: %m");
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- return c;
-}
+ r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
-static int dump_cache_item(sd_json_variant *item) {
+ r = bus_call_method(
+ bus,
+ bus_network_mgr,
+ "SetLinkMulticastDNS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- struct item_info {
- sd_json_variant *key;
- sd_json_variant *rrs;
- const char *type;
- uint64_t until;
- } item_info = {};
+ return log_error_errno(r, "Failed to set MulticastDNS configuration: %s", bus_error_message(&error, r));
+ }
- static const sd_json_dispatch_field dispatch_table[] = {
- { "key", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, offsetof(struct item_info, key), SD_JSON_MANDATORY },
- { "rrs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(struct item_info, rrs), 0 },
- { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct item_info, type), 0 },
- { "until", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct item_info, until), 0 },
- {},
- };
+ return 0;
+}
- _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
- int r, c = 0;
+static int verb_dns_over_tls(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
- r = sd_json_dispatch(item, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &item_info);
+ r = acquire_bus(&bus);
if (r < 0)
return r;
- r = dns_resource_key_from_json(item_info.key, &k);
- if (r < 0)
- return log_error_errno(r, "Failed to turn JSON data to resource key: %m");
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
- if (item_info.type)
- printf("%s %s%s%s\n", DNS_RESOURCE_KEY_TO_STRING(k), ansi_highlight_red(), item_info.type, ansi_normal());
- else {
- sd_json_variant *i;
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_DNS_OVER_TLS);
- JSON_VARIANT_ARRAY_FOREACH(i, item_info.rrs) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
- _cleanup_free_ void *data = NULL;
- sd_json_variant *raw;
- size_t size;
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_DNS_OVER_TLS);
- raw = sd_json_variant_by_key(i, "raw");
- if (!raw)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "raw field missing from RR JSON data.");
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- r = sd_json_variant_unbase64(raw, &data, &size);
- if (r < 0)
- return log_error_errno(r, "Unable to decode raw RR JSON data: %m");
+ r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSOverTLS", &error, NULL, "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
- r = dns_resource_record_new_from_raw(&rr, data, size);
- if (r < 0)
- return log_error_errno(r, "Failed to parse DNS data: %m");
+ r = bus_call_method(
+ bus,
+ bus_network_mgr,
+ "SetLinkDNSOverTLS",
+ &error,
+ NULL,
+ "is", arg_ifindex, argv[2]);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- printf("%s\n", dns_resource_record_to_string(rr));
- c++;
- }
+ return log_error_errno(r, "Failed to set DNSOverTLS configuration: %s", bus_error_message(&error, r));
}
- return c;
+ return 0;
}
-static int dump_cache_scope(sd_json_variant *scope) {
-
- struct scope_info {
- const char *protocol;
- int family;
- int ifindex;
- const char *ifname;
- sd_json_variant *cache;
- const char *dnssec_mode;
- const char *dns_over_tls_mode;
- } scope_info = {
- .family = AF_UNSPEC,
- };
- sd_json_variant *i;
- int r, c = 0;
-
- static const sd_json_dispatch_field dispatch_table[] = {
- { "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, protocol), SD_JSON_MANDATORY },
- { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(struct scope_info, family), 0 },
- { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(struct scope_info, ifindex), SD_JSON_RELAX },
- { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, ifname), 0 },
- { "cache", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(struct scope_info, cache), SD_JSON_MANDATORY },
- { "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, dnssec_mode), 0 },
- { "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, dns_over_tls_mode), 0 },
- {},
- };
+static int verb_dnssec(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
- r = sd_json_dispatch(scope, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &scope_info);
+ r = acquire_bus(&bus);
if (r < 0)
return r;
- printf("%sScope protocol=%s", ansi_underline(), scope_info.protocol);
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
- if (scope_info.family != AF_UNSPEC)
- printf(" family=%s", af_to_name(scope_info.family));
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_DNSSEC);
- if (scope_info.ifindex > 0)
- printf(" ifindex=%i", scope_info.ifindex);
- if (scope_info.ifname)
- printf(" ifname=%s", scope_info.ifname);
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_DNSSEC);
- if (dns_protocol_from_string(scope_info.protocol) == DNS_PROTOCOL_DNS) {
- if (scope_info.dnssec_mode)
- printf(" DNSSEC=%s", scope_info.dnssec_mode);
- if (scope_info.dns_over_tls_mode)
- printf(" DNSOverTLS=%s", scope_info.dns_over_tls_mode);
- }
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- printf("%s\n", ansi_normal());
+ r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
- JSON_VARIANT_ARRAY_FOREACH(i, scope_info.cache) {
- r = dump_cache_item(i);
- if (r < 0)
- return r;
+ r = bus_call_method(bus, bus_network_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- c += r;
+ return log_error_errno(r, "Failed to set DNSSEC configuration: %s", bus_error_message(&error, r));
}
- if (c == 0)
- printf("%sNo entries.%s\n\n", ansi_grey(), ansi_normal());
- else
- printf("\n");
+ return 0;
+}
+
+static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
+ int r;
+
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNSSECNegativeTrustAnchors");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "i", arg_ifindex);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(req, nta);
+ if (r < 0)
+ return bus_log_create_error(r);
- return 0;
+ return sd_bus_call(bus, req, 0, error, NULL);
}
-static int verb_show_cache(int argc, char *argv[], uintptr_t _data, void *userdata) {
- sd_json_variant *reply = NULL, *d = NULL;
- _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+static int verb_nta(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char **args;
+ bool clear;
int r;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
-
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
- if (r < 0)
- return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
-
- r = varlink_callbo_and_log(
- vl,
- "io.systemd.Resolve.Monitor.DumpCache",
- &reply,
- SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ r = acquire_bus(&bus);
if (r < 0)
return r;
- d = sd_json_variant_by_key(reply, "dump");
- if (!d)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "DumpCache() response is missing 'dump' key.");
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
+ if (r < 0)
+ return r;
+ }
- if (!sd_json_variant_is_array(d))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "DumpCache() response 'dump' field not an array");
+ if (arg_ifindex <= 0)
+ return status_all(STATUS_NTA);
- if (!sd_json_format_enabled(arg_json_format_flags)) {
- sd_json_variant *i;
+ if (argc < 3)
+ return status_ifindex(arg_ifindex, STATUS_NTA);
- JSON_VARIANT_ARRAY_FOREACH(i, d) {
- r = dump_cache_scope(i);
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
+
+ /* If only argument is the empty string, then call SetLinkDNSSECNegativeTrustAnchors()
+ * with an empty list, which will clear the list of domains for an interface. */
+ args = strv_skip(argv, 2);
+ clear = strv_equal(args, STRV_MAKE(""));
+
+ if (!clear)
+ STRV_FOREACH(p, args) {
+ r = dns_name_is_valid(*p);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to validate specified domain %s: %m", *p);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Domain not valid: %s",
+ *p);
}
- return 0;
- }
+ r = call_nta(bus, clear ? NULL : args, bus_resolve_mgr, &error);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
- return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL);
-}
+ r = call_nta(bus, clear ? NULL : args, bus_network_mgr, &error);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
-static int dump_server_state(sd_json_variant *server) {
- _cleanup_(table_unrefp) Table *table = NULL;
- TableCell *cell;
+ return log_error_errno(r, "Failed to set DNSSEC NTA configuration: %s", bus_error_message(&error, r));
+ }
- struct server_state {
- const char *server_name;
- const char *type;
- const char *ifname;
- int ifindex;
- const char *verified_feature_level;
- const char *possible_feature_level;
- const char *dnssec_mode;
- bool dnssec_supported;
- size_t received_udp_fragment_max;
- uint64_t n_failed_udp;
- uint64_t n_failed_tcp;
- bool packet_truncated;
- bool packet_bad_opt;
- bool packet_rrsig_missing;
- bool packet_invalid;
- bool packet_do_off;
- } server_state = {
- .ifindex = -1,
- };
+ return 0;
+}
+static int verb_revert_link(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
- static const sd_json_dispatch_field dispatch_table[] = {
- { "Server", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, server_name), SD_JSON_MANDATORY },
- { "Type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, type), SD_JSON_MANDATORY },
- { "Interface", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, ifname), 0 },
- { "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(struct server_state, ifindex), SD_JSON_RELAX },
- { "VerifiedFeatureLevel", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, verified_feature_level), 0 },
- { "PossibleFeatureLevel", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, possible_feature_level), 0 },
- { "DNSSECMode", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct server_state, dnssec_mode), SD_JSON_MANDATORY },
- { "DNSSECSupported", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, dnssec_supported), SD_JSON_MANDATORY },
- { "ReceivedUDPFragmentMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, received_udp_fragment_max), SD_JSON_MANDATORY },
- { "FailedUDPAttempts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, n_failed_udp), SD_JSON_MANDATORY },
- { "FailedTCPAttempts", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct server_state, n_failed_tcp), SD_JSON_MANDATORY },
- { "PacketTruncated", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_truncated), SD_JSON_MANDATORY },
- { "PacketBadOpt", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_bad_opt), SD_JSON_MANDATORY },
- { "PacketRRSIGMissing", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_rrsig_missing), SD_JSON_MANDATORY },
- { "PacketInvalid", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_invalid), SD_JSON_MANDATORY },
- { "PacketDoOff", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct server_state, packet_do_off), SD_JSON_MANDATORY },
- {},
- };
-
- r = sd_json_dispatch(server, dispatch_table, SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS, &server_state);
+ r = acquire_bus(&bus);
if (r < 0)
return r;
- table = table_new_vertical();
- if (!table)
- return log_oom();
-
- assert_se(cell = table_get_cell(table, 0, 0));
- (void) table_set_ellipsize_percent(table, cell, 100);
- (void) table_set_align_percent(table, cell, 0);
-
- r = table_add_cell_stringf(table, NULL, "Server: %s", server_state.server_name);
- if (r < 0)
- return table_log_add_error(r);
-
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_FIELD, "Type",
- TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_STRING, server_state.type);
- if (r < 0)
- return table_log_add_error(r);
-
- if (server_state.ifname) {
- r = table_add_many(table,
- TABLE_FIELD, "Interface",
- TABLE_STRING, server_state.ifname);
+ if (argc >= 2) {
+ r = ifname_mangle(argv[1]);
if (r < 0)
- return table_log_add_error(r);
+ return r;
}
- if (server_state.ifindex >= 0) {
- r = table_add_many(table,
- TABLE_FIELD, "Interface Index",
- TABLE_INT, server_state.ifindex);
- if (r < 0)
- return table_log_add_error(r);
- }
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
- if (server_state.verified_feature_level) {
- r = table_add_many(table,
- TABLE_FIELD, "Verified feature level",
- TABLE_STRING, server_state.verified_feature_level);
- if (r < 0)
- return table_log_add_error(r);
- }
+ (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
- if (server_state.possible_feature_level) {
- r = table_add_many(table,
- TABLE_FIELD, "Possible feature level",
- TABLE_STRING, server_state.possible_feature_level);
- if (r < 0)
- return table_log_add_error(r);
- }
+ r = bus_call_method(bus, bus_resolve_mgr, "RevertLink", &error, NULL, "i", arg_ifindex);
+ if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
+ sd_bus_error_free(&error);
- r = table_add_many(table,
- TABLE_FIELD, "DNSSEC Mode",
- TABLE_STRING, server_state.dnssec_mode,
- TABLE_FIELD, "DNSSEC Supported",
- TABLE_STRING, yes_no(server_state.dnssec_supported),
- TABLE_FIELD, "Maximum UDP fragment size received",
- TABLE_UINT64, server_state.received_udp_fragment_max,
- TABLE_FIELD, "Failed UDP attempts",
- TABLE_UINT64, server_state.n_failed_udp,
- TABLE_FIELD, "Failed TCP attempts",
- TABLE_UINT64, server_state.n_failed_tcp,
- TABLE_FIELD, "Seen truncated packet",
- TABLE_STRING, yes_no(server_state.packet_truncated),
- TABLE_FIELD, "Seen OPT RR getting lost",
- TABLE_STRING, yes_no(server_state.packet_bad_opt),
- TABLE_FIELD, "Seen RRSIG RR missing",
- TABLE_STRING, yes_no(server_state.packet_rrsig_missing),
- TABLE_FIELD, "Seen invalid packet",
- TABLE_STRING, yes_no(server_state.packet_invalid),
- TABLE_FIELD, "Server dropped DO flag",
- TABLE_STRING, yes_no(server_state.packet_do_off),
- TABLE_SET_ALIGN_PERCENT, 0,
- TABLE_EMPTY, TABLE_EMPTY);
+ r = bus_call_method(bus, bus_network_mgr, "RevertLinkDNS", &error, NULL, "i", arg_ifindex);
+ }
+ if (r < 0) {
+ if (arg_ifindex_permissive &&
+ sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_LINK))
+ return 0;
- if (r < 0)
- return table_log_add_error(r);
+ return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
+ }
- return table_print_or_warn(table);
+ return 0;
}
-static int verb_show_server_state(int argc, char *argv[], uintptr_t _data, void *userdata) {
- sd_json_variant *reply = NULL, *d = NULL;
- _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
+static int verb_log_level(int argc, char *argv[], uintptr_t _data, void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- (void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
-
- r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
- if (r < 0)
- return log_error_errno(r, "Failed to connect to query monitoring service /run/systemd/resolve/io.systemd.Resolve.Monitor: %m");
-
- r = varlink_callbo_and_log(
- vl,
- "io.systemd.Resolve.Monitor.DumpServerState",
- &reply,
- SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password));
+ r = acquire_bus(&bus);
if (r < 0)
return r;
- d = sd_json_variant_by_key(reply, "dump");
- if (!d)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "DumpCache() response is missing 'dump' key.");
-
- if (!sd_json_variant_is_array(d))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "DumpCache() response 'dump' field not an array");
-
- if (!sd_json_format_enabled(arg_json_format_flags)) {
- sd_json_variant *i;
-
- JSON_VARIANT_ARRAY_FOREACH(i, d) {
- r = dump_server_state(i);
- if (r < 0)
- return r;
- }
-
- return 0;
- }
+ assert(IN_SET(argc, 1, 2));
- return sd_json_variant_dump(d, arg_json_format_flags, NULL, NULL);
+ return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
}
static int parse_protocol(const char *arg) {