Merge pull request #24853 from poettering/resolved-monitor-fixes

resolved: various monitor fixes
This commit is contained in:
Luca Boccassi
2022-09-30 15:46:13 +01:00
committed by GitHub
28 changed files with 1103 additions and 214 deletions
+2
View File
@@ -53,6 +53,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X",
[SPECIAL_GLYPH_SIGMA] = "S",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
@@ -99,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
/* Single glyph in Unicode, two in ASCII */
[SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */
[SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */
/* Single glyph in Unicode, three in ASCII */
+1
View File
@@ -22,6 +22,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_MU,
SPECIAL_GLYPH_CHECK_MARK,
SPECIAL_GLYPH_CROSS_MARK,
SPECIAL_GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_DOWN,
+2
View File
@@ -790,6 +790,8 @@ global:
sd_device_monitor_set_description;
sd_device_monitor_get_description;
sd_event_set_signal_exit;
sd_id128_string_equal;
sd_hwdb_new_from_path;
+1
View File
@@ -99,6 +99,7 @@ struct sd_event_source {
sd_event_signal_handler_t callback;
struct signalfd_siginfo siginfo;
int sig;
bool unblock;
} signal;
struct {
sd_event_child_handler_t callback;
+120 -8
View File
@@ -153,6 +153,8 @@ struct sd_event {
LIST_HEAD(sd_event_source, sources);
sd_event_source *sigint_event_source, *sigterm_event_source;
usec_t last_run_usec, last_log_usec;
unsigned delays[sizeof(usec_t) * 8];
};
@@ -323,6 +325,9 @@ static sd_event *event_free(sd_event *e) {
assert(e);
e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
while ((s = e->sources)) {
assert(s->floating);
source_disconnect(s);
@@ -813,6 +818,7 @@ static void event_source_time_prioq_remove(
static void source_disconnect(sd_event_source *s) {
sd_event *event;
int r;
assert(s);
@@ -853,6 +859,20 @@ static void source_disconnect(sd_event_source *s) {
s->event->signal_sources[s->signal.sig] = NULL;
event_gc_signal_data(s->event, &s->priority, s->signal.sig);
if (s->signal.unblock) {
sigset_t new_ss;
if (sigemptyset(&new_ss) < 0)
log_debug_errno(errno, "Failed to reset signal set, ignoring: %m");
else if (sigaddset(&new_ss, s->signal.sig) < 0)
log_debug_errno(errno, "Failed to add signal %i to signal mask, ignoring: %m", s->signal.sig);
else {
r = pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL);
if (r != 0)
log_debug_errno(r, "Failed to unblock signal %i, ignoring: %m", s->signal.sig);
}
}
}
break;
@@ -1328,23 +1348,38 @@ _public_ int sd_event_add_signal(
_cleanup_(source_freep) sd_event_source *s = NULL;
struct signal_data *d;
sigset_t new_ss;
bool block_it;
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(SIGNAL_VALID(sig), -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
/* Let's make sure our special flag stays outside of the valid signal range */
assert_cc(_NSIG < SD_EVENT_SIGNAL_PROCMASK);
if (sig & SD_EVENT_SIGNAL_PROCMASK) {
sig &= ~SD_EVENT_SIGNAL_PROCMASK;
assert_return(SIGNAL_VALID(sig), -EINVAL);
block_it = true;
} else {
assert_return(SIGNAL_VALID(sig), -EINVAL);
r = signal_is_blocked(sig);
if (r < 0)
return r;
if (r == 0)
return -EBUSY;
block_it = false;
}
if (!callback)
callback = signal_exit_callback;
r = signal_is_blocked(sig);
if (r < 0)
return r;
if (r == 0)
return -EBUSY;
if (!e->signal_sources) {
e->signal_sources = new0(sd_event_source*, _NSIG);
if (!e->signal_sources)
@@ -1363,9 +1398,34 @@ _public_ int sd_event_add_signal(
e->signal_sources[sig] = s;
if (block_it) {
sigset_t old_ss;
if (sigemptyset(&new_ss) < 0)
return -errno;
if (sigaddset(&new_ss, sig) < 0)
return -errno;
r = pthread_sigmask(SIG_BLOCK, &new_ss, &old_ss);
if (r != 0)
return -r;
r = sigismember(&old_ss, sig);
if (r < 0)
return -errno;
s->signal.unblock = !r;
} else
s->signal.unblock = false;
r = event_make_signal_data(e, sig, &d);
if (r < 0)
if (r < 0) {
if (s->signal.unblock)
(void) pthread_sigmask(SIG_UNBLOCK, &new_ss, NULL);
return r;
}
/* Use the signal name as description for the event source by default */
(void) sd_event_source_set_description(s, signal_to_string(sig));
@@ -4558,3 +4618,55 @@ _public_ int sd_event_source_is_ratelimited(sd_event_source *s) {
return s->ratelimited;
}
_public_ int sd_event_set_signal_exit(sd_event *e, int b) {
bool change = false;
int r;
assert_return(e, -EINVAL);
if (b) {
/* We want to maintain pointers to these event sources, so that we can destroy them when told
* so. But we also don't want them to pin the event loop itself. Hence we mark them as
* floating after creation (and undo this before deleting them again). */
if (!e->sigint_event_source) {
r = sd_event_add_signal(e, &e->sigint_event_source, SIGINT | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0)
return r;
assert(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
change = true;
}
if (!e->sigterm_event_source) {
r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0) {
if (change) {
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
}
return r;
}
assert(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
change = true;
}
} else {
if (e->sigint_event_source) {
assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
change = true;
}
if (e->sigterm_event_source) {
assert(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
change = true;
}
}
return change;
}
+249 -15
View File
@@ -15,11 +15,13 @@
#include "bus-map-properties.h"
#include "bus-message-util.h"
#include "dns-domain.h"
#include "errno-list.h"
#include "escape.h"
#include "format-table.h"
#include "format-util.h"
#include "gcrypt-util.h"
#include "hostname-util.h"
#include "json.h"
#include "main-func.h"
#include "missing_network.h"
#include "netlink-util.h"
@@ -41,6 +43,7 @@
#include "strv.h"
#include "terminal-util.h"
#include "utf8.h"
#include "varlink.h"
#include "verb-log-control.h"
#include "verbs.h"
@@ -51,6 +54,7 @@ static uint16_t arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
bool arg_ifindex_permissive = false; /* If true, don't generate an error if the specified interface index doesn't exist */
static const char *arg_service_family = NULL;
@@ -395,20 +399,9 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
static int output_rr_packet(const void *d, size_t l, int ifindex) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
int r;
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
return log_oom();
p->refuse_compression = true;
r = dns_packet_append_blob(p, d, l, NULL);
if (r < 0)
return log_oom();
r = dns_packet_read_rr(p, &rr, NULL, NULL);
r = dns_resource_record_new_from_raw(&rr, d, l);
if (r < 0)
return log_error_errno(r, "Failed to parse RR: %m");
@@ -2514,6 +2507,227 @@ static int verb_log_level(int argc, char *argv[], void *userdata) {
return verb_log_control_common(bus, "org.freedesktop.resolve1", argv[0], argc == 2 ? argv[1] : NULL);
}
static int monitor_rkey_from_json(JsonVariant *v, DnsResourceKey **ret_key) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
uint16_t type = 0, class = 0;
const char *name = NULL;
int r;
JsonDispatch dispatch_table[] = {
{ "class", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&class), JSON_MANDATORY },
{ "type", JSON_VARIANT_INTEGER, json_dispatch_uint16, PTR_TO_SIZE(&type), JSON_MANDATORY },
{ "name", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&name), JSON_MANDATORY },
{}
};
assert(v);
assert(ret_key);
r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
if (r < 0)
return r;
key = dns_resource_key_new(class, type, name);
if (!key)
return -ENOMEM;
*ret_key = TAKE_PTR(key);
return 0;
}
static int print_question(char prefix, const char *color, JsonVariant *question) {
JsonVariant *q = NULL;
int r;
assert(color);
JSON_VARIANT_ARRAY_FOREACH(q, question) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
char buf[DNS_RESOURCE_KEY_STRING_MAX];
r = monitor_rkey_from_json(q, &key);
if (r < 0) {
log_warning_errno(r, "Received monitor message with invalid question key, ignoring: %m");
continue;
}
printf("%s%s %c%s: %s\n",
color,
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
prefix,
ansi_normal(),
dns_resource_key_to_string(key, buf, sizeof(buf)));
}
return 0;
}
static int print_answer(JsonVariant *answer) {
JsonVariant *a;
int r;
JSON_VARIANT_ARRAY_FOREACH(a, answer) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_free_ void *d = NULL;
JsonVariant *jraw;
const char *s;
size_t l;
jraw = json_variant_by_key(a, "raw");
if (!jraw) {
log_warning("Received monitor answer lacking valid raw data, ignoring.");
continue;
}
r = 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 = dns_resource_record_new_from_raw(&rr, d, l);
if (r < 0) {
log_warning_errno(r, "Failed to parse monitor answer RR, ingoring: %m");
continue;
}
s = dns_resource_record_to_string(rr);
if (!s)
return log_oom();
printf("%s%s A%s: %s\n",
ansi_highlight_yellow(),
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
ansi_normal(),
s);
}
return 0;
}
static void monitor_query_dump(JsonVariant *v) {
_cleanup_(json_variant_unrefp) JsonVariant *question = NULL, *answer = NULL, *collected_questions = NULL;
int rcode = -1, error = 0, r;
const char *state = NULL;
assert(v);
JsonDispatch dispatch_table[] = {
{ "question", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&question), JSON_MANDATORY },
{ "answer", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&answer), 0 },
{ "collectedQuestions", JSON_VARIANT_ARRAY, json_dispatch_variant, PTR_TO_SIZE(&collected_questions), 0 },
{ "state", JSON_VARIANT_STRING, json_dispatch_const_string, PTR_TO_SIZE(&state), JSON_MANDATORY },
{ "rcode", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&rcode), 0 },
{ "errno", JSON_VARIANT_INTEGER, json_dispatch_int, PTR_TO_SIZE(&error), 0 },
{}
};
r = json_dispatch(v, dispatch_table, NULL, 0, NULL);
if (r < 0)
return (void) log_warning("Received malformed monitor message, ignoring.");
/* First show the current question */
print_question('Q', ansi_highlight_cyan(), question);
/* And then show the questions that led to this one in case this was a CNAME chain */
print_question('C', ansi_highlight_grey(), collected_questions);
printf("%s%s S%s: %s\n",
streq_ptr(state, "success") ? ansi_highlight_green() : ansi_highlight_red(),
special_glyph(SPECIAL_GLYPH_ARROW_LEFT),
ansi_normal(),
strna(streq_ptr(state, "errno") ? errno_to_name(error) :
streq_ptr(state, "rcode-failure") ? dns_rcode_to_string(rcode) :
state));
print_answer(answer);
}
static int monitor_reply(
Varlink *link,
JsonVariant *parameters,
const char *error_id,
VarlinkReplyFlags flags,
void *userdata) {
assert(link);
if (error_id) {
bool disconnect;
disconnect = streq(error_id, VARLINK_ERROR_DISCONNECTED);
if (disconnect)
log_info("Disconnected.");
else
log_error("Varlink error: %s", error_id);
(void) sd_event_exit(ASSERT_PTR(varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
return 0;
}
if (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 (arg_json_format_flags & JSON_FORMAT_OFF) {
monitor_query_dump(parameters);
printf("\n");
} else
json_variant_dump(parameters, arg_json_format_flags, NULL, NULL);
fflush(stdout);
return 0;
}
static int verb_monitor(int argc, char *argv[], void *userdata) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(varlink_unrefp) Varlink *vl = NULL;
int r, c;
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
r = sd_event_set_signal_exit(event, true);
if (r < 0)
return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
r = 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_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 time-out: %m");
r = 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 = varlink_bind_reply(vl, monitor_reply);
if (r < 0)
return log_error_errno(r, "Failed to bind reply callback to varlink connection: %m");
r = varlink_observe(vl, "io.systemd.Resolve.Monitor.SubscribeQueryResults", NULL);
if (r < 0)
return log_error_errno(r, "Failed to issue SubscribeQueryResults() varlink call: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
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 void help_protocol_types(void) {
if (arg_legend)
puts("Known protocol types:");
@@ -2619,6 +2833,7 @@ static int native_help(void) {
" reset-statistics Reset resolver statistics\n"
" flush-caches Flush all local DNS caches\n"
" reset-server-features Forget learnt DNS server feature levels\n"
" monitor Monitor DNS queries\n"
" dns [LINK [SERVER...]] Get/set per-interface DNS server address\n"
" domain [LINK [DOMAIN...]] Get/set per-interface search domain\n"
" default-route [LINK [BOOL]] Get/set per-interface default route flag\n"
@@ -2647,11 +2862,16 @@ static int native_help(void) {
" --cache=BOOL Allow response from cache (default: yes)\n"
" --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
" records (default: yes)\n"
" --trust-anchor=BOOL Allow response from local trust anchor (default: yes)\n"
" --trust-anchor=BOOL Allow response from local trust anchor (default:\n"
" yes)\n"
" --network=BOOL Allow response from network (default: yes)\n"
" --search=BOOL Use search domains for single-label names (default: yes)\n"
" --search=BOOL Use search domains for single-label names (default:\n"
" yes)\n"
" --raw[=payload|packet] Dump the answer as binary data\n"
" --legend=BOOL Print headers and additional info (default: yes)\n"
" --json=MODE Output as JSON\n"
" -j Same as --json=pretty on tty, --json=short\n"
" otherwise\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -2998,6 +3218,7 @@ static int native_parse_argv(int argc, char *argv[]) {
ARG_RAW,
ARG_SEARCH,
ARG_NO_PAGER,
ARG_JSON,
};
static const struct option options[] = {
@@ -3020,6 +3241,7 @@ static int native_parse_argv(int argc, char *argv[]) {
{ "raw", optional_argument, NULL, ARG_RAW },
{ "search", required_argument, NULL, ARG_SEARCH },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "json", required_argument, NULL, ARG_JSON },
{}
};
@@ -3028,7 +3250,7 @@ static int native_parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "h46i:t:c:p:j", options, NULL)) >= 0)
switch (c) {
case 'h':
@@ -3203,6 +3425,17 @@ static int native_parse_argv(int argc, char *argv[]) {
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
case 'j':
arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
break;
case '?':
return -EINVAL;
@@ -3246,6 +3479,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
{ "nta", VERB_ANY, VERB_ANY, 0, verb_nta },
{ "revert", VERB_ANY, 2, 0, verb_revert_link },
{ "log-level", VERB_ANY, 2, 0, verb_log_level },
{ "monitor", VERB_ANY, 1, 0, verb_monitor },
{}
};
-1
View File
@@ -2096,7 +2096,6 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0),
SD_BUS_PROPERTY("ResolvConfMode", "s", bus_property_get_resolv_conf_mode, 0, 0),
SD_BUS_PROPERTY("Monitor", "b", bus_property_get_bool, offsetof(Manager, enable_varlink_notifications), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_ARGS("ResolveHostname",
SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags),
+30 -2
View File
@@ -397,6 +397,7 @@ DnsQuery *dns_query_free(DnsQuery *q) {
dns_question_unref(q->question_idna);
dns_question_unref(q->question_utf8);
dns_packet_unref(q->question_bypass);
dns_question_unref(q->collected_questions);
dns_query_reset_answer(q);
@@ -585,8 +586,7 @@ void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
q->state = state;
if (q->question_utf8 && state == DNS_TRANSACTION_SUCCESS && set_size(q->manager->varlink_subscription) > 0)
(void) send_dns_notification(q->manager, q->answer, dns_question_first_name(q->question_utf8));
(void) manager_monitor_send(q->manager, q->state, q->answer_rcode, q->answer_errno, q->question_idna, q->question_utf8, q->collected_questions, q->answer);
dns_query_stop(q);
if (q->complete)
@@ -980,6 +980,26 @@ void dns_query_ready(DnsQuery *q) {
dns_query_accept(q, bad);
}
static int dns_query_collect_question(DnsQuery *q, DnsQuestion *question) {
_cleanup_(dns_question_unrefp) DnsQuestion *merged = NULL;
int r;
assert(q);
if (dns_question_size(question) == 0)
return 0;
/* When redirecting, save the first element in the chain, for informational purposes when monitoring */
r = dns_question_merge(q->collected_questions, question, &merged);
if (r < 0)
return r;
dns_question_unref(q->collected_questions);
q->collected_questions = TAKE_PTR(merged);
return 0;
}
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
_cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
int r, k;
@@ -1029,6 +1049,14 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
/* Turn off searching for the new name */
q->flags |= SD_RESOLVED_NO_SEARCH;
r = dns_query_collect_question(q, q->question_idna);
if (r < 0)
return r;
r = dns_query_collect_question(q, q->question_utf8);
if (r < 0)
return r;
/* Install the redirected question */
dns_question_unref(q->question_idna);
q->question_idna = TAKE_PTR(nq_idna);
+5
View File
@@ -52,6 +52,11 @@ struct DnsQuery {
* here, and use that instead. */
DnsPacket *question_bypass;
/* When we follow a CNAME redirect, we save the original question here, for informational/monitoring
* purposes. We'll keep adding to this whenever we go one step in the redirect, so that in the end
* this will contain the complete set of CNAME questions. */
DnsQuestion *collected_questions;
uint64_t flags;
int ifindex;
+58
View File
@@ -50,6 +50,19 @@ int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags f
return 0;
}
static int dns_question_add_raw_all(DnsQuestion *a, DnsQuestion *b) {
DnsQuestionItem *item;
int r;
DNS_QUESTION_FOREACH_ITEM(item, b) {
r = dns_question_add_raw(a, item->key, item->flags);
if (r < 0)
return r;
}
return 0;
}
int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
DnsQuestionItem *item;
int r;
@@ -71,6 +84,19 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags
return dns_question_add_raw(q, key, flags);
}
static int dns_question_add_all(DnsQuestion *a, DnsQuestion *b) {
DnsQuestionItem *item;
int r;
DNS_QUESTION_FOREACH_ITEM(item, b) {
r = dns_question_add(a, item->key, item->flags);
if (r < 0)
return r;
}
return 0;
}
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
DnsResourceKey *key;
int r;
@@ -486,3 +512,35 @@ void dns_question_dump(DnsQuestion *question, FILE *f) {
fputc('\n', f);
}
}
int dns_question_merge(DnsQuestion *a, DnsQuestion *b, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *k = NULL;
int r;
assert(ret);
if (a == b || dns_question_size(b) <= 0) {
*ret = dns_question_ref(a);
return 0;
}
if (dns_question_size(a) <= 0) {
*ret = dns_question_ref(b);
return 0;
}
k = dns_question_new(dns_question_size(a) + dns_question_size(b));
if (!k)
return -ENOMEM;
r = dns_question_add_raw_all(k, a);
if (r < 0)
return r;
r = dns_question_add_all(k, b);
if (r < 0)
return r;
*ret = TAKE_PTR(k);
return 0;
}
+2
View File
@@ -59,6 +59,8 @@ static inline bool dns_question_isempty(DnsQuestion *q) {
return dns_question_size(q) <= 0;
}
int dns_question_merge(DnsQuestion *a, DnsQuestion *b, DnsQuestion **ret);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
#define _DNS_QUESTION_FOREACH(u, k, q) \
+259
View File
@@ -1832,6 +1832,265 @@ int dns_txt_item_new_empty(DnsTxtItem **ret) {
return 0;
}
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
int r;
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
p->refuse_compression = true;
r = dns_packet_append_blob(p, data, size, NULL);
if (r < 0)
return r;
return dns_packet_read_rr(p, ret, NULL, NULL);
}
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret) {
assert(key);
assert(ret);
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("class", JSON_BUILD_INTEGER(key->class)),
JSON_BUILD_PAIR("type", JSON_BUILD_INTEGER(key->type)),
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(dns_resource_key_name(key)))));
}
static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
unsigned t;
int r;
assert(b);
assert(ret);
BITMAP_FOREACH(t, b) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = json_variant_new_unsigned(&v, t);
if (r < 0)
return r;
r = json_variant_append_array(&l, v);
if (r < 0)
return r;
}
if (!l)
return json_variant_new_array(ret, NULL, 0);
*ret = TAKE_PTR(l);
return 0;
}
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
int r;
assert(rr);
assert(ret);
r = dns_resource_key_to_json(rr->key, &k);
if (r < 0)
return r;
switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
case DNS_TYPE_SRV:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->srv.priority)),
JSON_BUILD_PAIR("weight", JSON_BUILD_UNSIGNED(rr->srv.weight)),
JSON_BUILD_PAIR("port", JSON_BUILD_UNSIGNED(rr->srv.port)),
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(rr->srv.name))));
case DNS_TYPE_PTR:
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(rr->ptr.name))));
case DNS_TYPE_HINFO:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("cpu", JSON_BUILD_STRING(rr->hinfo.cpu)),
JSON_BUILD_PAIR("os", JSON_BUILD_STRING(rr->hinfo.os))));
case DNS_TYPE_SPF:
case DNS_TYPE_TXT: {
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
LIST_FOREACH(items, i, rr->txt.items) {
_cleanup_(json_variant_unrefp) JsonVariant *b = NULL;
r = json_variant_new_octescape(&b, i->data, i->length);
if (r < 0)
return r;
r = json_variant_append_array(&l, b);
if (r < 0)
return r;
}
if (!l) {
r = json_variant_new_array(&l, NULL, 0);
if (r < 0)
return r;
}
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("items", JSON_BUILD_VARIANT(l))));
}
case DNS_TYPE_A:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("address", JSON_BUILD_IN4_ADDR(&rr->a.in_addr))));
case DNS_TYPE_AAAA:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("address", JSON_BUILD_IN6_ADDR(&rr->aaaa.in6_addr))));
case DNS_TYPE_SOA:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("mname", JSON_BUILD_STRING(rr->soa.mname)),
JSON_BUILD_PAIR("rname", JSON_BUILD_STRING(rr->soa.rname)),
JSON_BUILD_PAIR("serial", JSON_BUILD_UNSIGNED(rr->soa.serial)),
JSON_BUILD_PAIR("refresh", JSON_BUILD_UNSIGNED(rr->soa.refresh)),
JSON_BUILD_PAIR("expire", JSON_BUILD_UNSIGNED(rr->soa.retry)),
JSON_BUILD_PAIR("minimum", JSON_BUILD_UNSIGNED(rr->soa.minimum))));
case DNS_TYPE_MX:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->mx.priority)),
JSON_BUILD_PAIR("exchange", JSON_BUILD_STRING(rr->mx.exchange))));
case DNS_TYPE_LOC:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("version", JSON_BUILD_UNSIGNED(rr->loc.version)),
JSON_BUILD_PAIR("size", JSON_BUILD_UNSIGNED(rr->loc.size)),
JSON_BUILD_PAIR("horiz_pre", JSON_BUILD_UNSIGNED(rr->loc.horiz_pre)),
JSON_BUILD_PAIR("vert_pre", JSON_BUILD_UNSIGNED(rr->loc.vert_pre)),
JSON_BUILD_PAIR("latitude", JSON_BUILD_UNSIGNED(rr->loc.latitude)),
JSON_BUILD_PAIR("longitude", JSON_BUILD_UNSIGNED(rr->loc.longitude)),
JSON_BUILD_PAIR("altitude", JSON_BUILD_UNSIGNED(rr->loc.altitude))));
case DNS_TYPE_DS:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("keyTag", JSON_BUILD_UNSIGNED(rr->ds.key_tag)),
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->ds.algorithm)),
JSON_BUILD_PAIR("digestType", JSON_BUILD_UNSIGNED(rr->ds.digest_type)),
JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(rr->ds.digest, rr->ds.digest_size))));
case DNS_TYPE_SSHFP:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->sshfp.algorithm)),
JSON_BUILD_PAIR("fptype", JSON_BUILD_UNSIGNED(rr->sshfp.fptype)),
JSON_BUILD_PAIR("fingerprint", JSON_BUILD_HEX(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size))));
case DNS_TYPE_DNSKEY:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->dnskey.flags)),
JSON_BUILD_PAIR("protocol", JSON_BUILD_UNSIGNED(rr->dnskey.protocol)),
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->dnskey.algorithm)),
JSON_BUILD_PAIR("dnskey", JSON_BUILD_BASE64(rr->dnskey.key, rr->dnskey.key_size))));
case DNS_TYPE_RRSIG:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("signer", JSON_BUILD_STRING(rr->rrsig.signer)),
JSON_BUILD_PAIR("typeCovered", JSON_BUILD_UNSIGNED(rr->rrsig.type_covered)),
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->rrsig.algorithm)),
JSON_BUILD_PAIR("labels", JSON_BUILD_UNSIGNED(rr->rrsig.labels)),
JSON_BUILD_PAIR("originalTtl", JSON_BUILD_UNSIGNED(rr->rrsig.original_ttl)),
JSON_BUILD_PAIR("expiration", JSON_BUILD_UNSIGNED(rr->rrsig.expiration)),
JSON_BUILD_PAIR("inception", JSON_BUILD_UNSIGNED(rr->rrsig.inception)),
JSON_BUILD_PAIR("keyTag", JSON_BUILD_UNSIGNED(rr->rrsig.key_tag)),
JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(rr->rrsig.signature, rr->rrsig.signature_size))));
case DNS_TYPE_NSEC: {
_cleanup_(json_variant_unrefp) JsonVariant *bm = NULL;
r = type_bitmap_to_json(rr->nsec.types, &bm);
if (r < 0)
return r;
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("nextDomain", JSON_BUILD_STRING(rr->nsec.next_domain_name)),
JSON_BUILD_PAIR("types", JSON_BUILD_VARIANT(bm))));
}
case DNS_TYPE_NSEC3: {
_cleanup_(json_variant_unrefp) JsonVariant *bm = NULL;
r = type_bitmap_to_json(rr->nsec3.types, &bm);
if (r < 0)
return r;
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("algorithm", JSON_BUILD_UNSIGNED(rr->nsec3.algorithm)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->nsec3.flags)),
JSON_BUILD_PAIR("iterations", JSON_BUILD_UNSIGNED(rr->nsec3.iterations)),
JSON_BUILD_PAIR("salt", JSON_BUILD_HEX(rr->nsec3.salt, rr->nsec3.salt_size)),
JSON_BUILD_PAIR("hash", JSON_BUILD_BASE32HEX(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size)),
JSON_BUILD_PAIR("types", JSON_BUILD_VARIANT(bm))));
}
case DNS_TYPE_TLSA:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("certUsage", JSON_BUILD_UNSIGNED(rr->tlsa.cert_usage)),
JSON_BUILD_PAIR("selector", JSON_BUILD_UNSIGNED(rr->tlsa.selector)),
JSON_BUILD_PAIR("matchingType", JSON_BUILD_UNSIGNED(rr->tlsa.matching_type)),
JSON_BUILD_PAIR("data", JSON_BUILD_HEX(rr->tlsa.data, rr->tlsa.data_size))));
case DNS_TYPE_CAA:
return json_build(ret,
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("key", JSON_BUILD_VARIANT(k)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(rr->caa.flags)),
JSON_BUILD_PAIR("tag", JSON_BUILD_STRING(rr->caa.tag)),
JSON_BUILD_PAIR("value", JSON_BUILD_OCTESCAPE(rr->caa.value, rr->caa.value_size))));
default:
/* Can't provide broken-down format */
*ret = NULL;
return 0;
}
}
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
+6
View File
@@ -8,6 +8,7 @@
#include "dns-type.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "json.h"
#include "list.h"
#include "string-util.h"
#include "time-util.h"
@@ -364,6 +365,11 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);
int dns_txt_item_new_empty(DnsTxtItem **ret);
int dns_resource_record_new_from_raw(DnsResourceRecord **ret, const void *data, size_t size);
int dns_resource_key_to_json(DnsResourceKey *key, JsonVariant **ret);
int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret);
void dns_resource_record_hash_func(const DnsResourceRecord *i, struct siphash *state);
int dns_resource_record_compare_func(const DnsResourceRecord *x, const DnsResourceRecord *y);
-1
View File
@@ -32,4 +32,3 @@ Resolve.ReadEtcHosts, config_parse_bool, 0,
Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
Resolve.CacheFromLocalhost, config_parse_bool, 0, offsetof(Manager, cache_from_localhost)
Resolve.Monitor, config_parse_bool, 0, offsetof(Manager, enable_varlink_notifications)
+77 -39
View File
@@ -1043,62 +1043,100 @@ static int manager_ipv6_send(
return sendmsg_loop(fd, &mh, 0);
}
int send_dns_notification(Manager *m, DnsAnswer *answer, const char *query_name) {
_cleanup_free_ char *normalized = NULL;
DnsResourceRecord *rr;
int ifindex, r;
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
static int dns_question_to_json(DnsQuestion *q, JsonVariant **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
DnsResourceKey *key;
int r;
assert(ret);
DNS_QUESTION_FOREACH(key, q) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
r = dns_resource_key_to_json(key, &v);
if (r < 0)
return r;
r = json_variant_append_array(&l, v);
if (r < 0)
return r;
}
*ret = TAKE_PTR(l);
return 0;
}
int manager_monitor_send(
Manager *m,
int state,
int rcode,
int error,
DnsQuestion *question_idna,
DnsQuestion *question_utf8,
DnsQuestion *collected_questions,
DnsAnswer *answer) {
_cleanup_(json_variant_unrefp) JsonVariant *jquestion = NULL, *jcollected_questions = NULL, *janswer = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *merged = NULL;
Varlink *connection;
DnsAnswerItem *rri;
int r;
assert(m);
if (set_isempty(m->varlink_subscription))
return 0;
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, answer) {
_cleanup_(json_variant_unrefp) JsonVariant *entry = NULL;
/* Merge both questions format into one */
r = dns_question_merge(question_idna, question_utf8, &merged);
if (r < 0)
return log_error_errno(r, "Failed to merge UTF8/IDNA questions: %m");
if (rr->key->type == DNS_TYPE_A) {
struct in_addr *addr = &rr->a.in_addr;
r = json_build(&entry,
JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
JSON_BUILD_PAIR_INTEGER("family", AF_INET),
JSON_BUILD_PAIR_IN4_ADDR("address", addr),
JSON_BUILD_PAIR_STRING("type", "A")));
} else if (rr->key->type == DNS_TYPE_AAAA) {
struct in6_addr *addr6 = &rr->aaaa.in6_addr;
r = json_build(&entry,
JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
JSON_BUILD_PAIR_INTEGER("family", AF_INET6),
JSON_BUILD_PAIR_IN6_ADDR("address", addr6),
JSON_BUILD_PAIR_STRING("type", "AAAA")));
} else
continue;
if (r < 0) {
log_debug_errno(r, "Failed to build json object: %m");
continue;
}
/* Convert the current primary question to JSON */
r = dns_question_to_json(merged, &jquestion);
if (r < 0)
return log_error_errno(r, "Failed to convert question to JSON: %m");
r = json_variant_append_array(&array, entry);
/* Generate a JSON array of the questions preceeding the current one in the CNAME chain */
r = dns_question_to_json(collected_questions, &jcollected_questions);
if (r < 0)
return log_error_errno(r, "Failed to convert question to JSON: %m");
DNS_ANSWER_FOREACH_ITEM(rri, answer) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
r = dns_resource_record_to_json(rri->rr, &v);
if (r < 0)
return log_error_errno(r, "Failed to convert answer resource record to JSON: %m");
r = dns_resource_record_to_wire_format(rri->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
if (r < 0)
return log_error_errno(r, "Failed to generate RR wire format: %m");
r = json_build(&w, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rri->rr->wire_format, rri->rr->wire_format_size)),
JSON_BUILD_PAIR_CONDITION(rri->ifindex > 0, "ifindex", JSON_BUILD_INTEGER(rri->ifindex))));
if (r < 0)
return log_error_errno(r, "Failed to make answer RR object: %m");
r = json_variant_append_array(&janswer, w);
if (r < 0)
return log_debug_errno(r, "Failed to append notification entry to array: %m");
}
if (json_variant_is_blank_object(array))
return 0;
r = dns_name_normalize(query_name, 0, &normalized);
if (r < 0)
return log_debug_errno(r, "Failed to normalize query name: %m");
SET_FOREACH(connection, m->varlink_subscription) {
r = varlink_notifyb(connection,
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("addresses",
JSON_BUILD_VARIANT(array)),
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized))));
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("state", JSON_BUILD_STRING(dns_transaction_state_to_string(state))),
JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_RCODE_FAILURE, "rcode", JSON_BUILD_INTEGER(rcode)),
JSON_BUILD_PAIR_CONDITION(state == DNS_TRANSACTION_ERRNO, "errno", JSON_BUILD_INTEGER(error)),
JSON_BUILD_PAIR("question", JSON_BUILD_VARIANT(jquestion)),
JSON_BUILD_PAIR_CONDITION(jcollected_questions, "collectedQuestions", JSON_BUILD_VARIANT(jcollected_questions)),
JSON_BUILD_PAIR_CONDITION(janswer, "answer", JSON_BUILD_VARIANT(janswer))));
if (r < 0)
log_debug_errno(r, "Failed to send notification, ignoring: %m");
log_debug_errno(r, "Failed to send monitor event, ignoring: %m");
}
return 0;
}
+2 -3
View File
@@ -41,7 +41,6 @@ struct Manager {
DnsOverTlsMode dns_over_tls_mode;
DnsCacheMode enable_cache;
bool cache_from_localhost;
bool enable_varlink_notifications;
DnsStubListenerMode dns_stub_listener_mode;
#if ENABLE_DNS_OVER_TLS
@@ -148,7 +147,7 @@ struct Manager {
Hashmap *polkit_registry;
VarlinkServer *varlink_server;
VarlinkServer *varlink_notification_server;
VarlinkServer *varlink_monitor_server;
Set *varlink_subscription;
@@ -168,7 +167,7 @@ int manager_start(Manager *m);
uint32_t manager_find_mtu(Manager *m);
int send_dns_notification(Manager *m, DnsAnswer *answer, const char *query_name);
int manager_monitor_send(Manager *m, int state, int rcode, int error, DnsQuestion *question_idna, DnsQuestion *question_utf8, DnsQuestion *collected_questions, DnsAnswer *answer);
int manager_write(Manager *m, int fd, DnsPacket *p);
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p);
+22 -6
View File
@@ -546,6 +546,13 @@ static int vl_method_subscribe_dns_resolves(Varlink *link, JsonVariant *paramete
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
/* Send a ready message to the connecting client, to indicate that we are now listinening, and all
* queries issued after the point the client sees this will also be reported to the client. */
r = varlink_notifyb(link,
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("ready", JSON_BUILD_BOOLEAN(true))));
if (r < 0)
return log_error_errno(r, "Failed to report monitor to be established: %m");
r = set_ensure_put(&m->varlink_subscription, NULL, link);
if (r < 0)
return log_error_errno(r, "Failed to add subscription to set: %m");
@@ -556,13 +563,13 @@ static int vl_method_subscribe_dns_resolves(Varlink *link, JsonVariant *paramete
return 1;
}
static int varlink_notification_server_init(Manager *m) {
static int varlink_monitor_server_init(Manager *m) {
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
int r;
assert(m);
if (!m->enable_varlink_notifications || m->varlink_notification_server)
if (m->varlink_monitor_server)
return 0;
r = varlink_server_new(&server, VARLINK_SERVER_ROOT_ONLY);
@@ -590,12 +597,12 @@ static int varlink_notification_server_init(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_notification_server = TAKE_PTR(server);
m->varlink_monitor_server = TAKE_PTR(server);
return 0;
}
int manager_varlink_init(Manager *m) {
static int varlink_main_server_init(Manager *m) {
_cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
int r;
@@ -630,8 +637,17 @@ int manager_varlink_init(Manager *m) {
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_server = TAKE_PTR(s);
return 0;
}
r = varlink_notification_server_init(m);
int manager_varlink_init(Manager *m) {
int r;
r = varlink_main_server_init(m);
if (r < 0)
return r;
r = varlink_monitor_server_init(m);
if (r < 0)
return r;
@@ -642,5 +658,5 @@ void manager_varlink_done(Manager *m) {
assert(m);
m->varlink_server = varlink_server_unref(m->varlink_server);
m->varlink_notification_server = varlink_server_unref(m->varlink_notification_server);
m->varlink_monitor_server = varlink_server_unref(m->varlink_monitor_server);
}
+79 -33
View File
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "float.h"
@@ -437,6 +438,19 @@ int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n) {
return json_variant_new_stringn(ret, s, k);
}
int json_variant_new_base32hex(JsonVariant **ret, const void *p, size_t n) {
_cleanup_free_ char *s = NULL;
assert_return(ret, -EINVAL);
assert_return(n == 0 || p, -EINVAL);
s = base32hexmem(p, n, false);
if (!s)
return -ENOMEM;
return json_variant_new_string(ret, s);
}
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
_cleanup_free_ char *s = NULL;
@@ -450,6 +464,19 @@ int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
return json_variant_new_stringn(ret, s, n*2);
}
int json_variant_new_octescape(JsonVariant **ret, const void *p, size_t n) {
_cleanup_free_ char *s = NULL;
assert_return(ret, -EINVAL);
assert_return(n == 0 || p, -EINVAL);
s = octescape(p, n);
if (!s)
return -ENOMEM;
return json_variant_new_string(ret, s);
}
int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
}
@@ -3543,7 +3570,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
break;
}
case _JSON_BUILD_BASE64: {
case _JSON_BUILD_BASE64:
case _JSON_BUILD_BASE32HEX:
case _JSON_BUILD_HEX:
case _JSON_BUILD_OCTESCAPE: {
const void *p;
size_t n;
@@ -3556,37 +3586,10 @@ int json_buildv(JsonVariant **ret, va_list ap) {
n = va_arg(ap, size_t);
if (current->n_suppress == 0) {
r = json_variant_new_base64(&add, p, n);
if (r < 0)
goto finish;
}
n_subtract = 1;
if (current->expect == EXPECT_TOPLEVEL)
current->expect = EXPECT_END;
else if (current->expect == EXPECT_OBJECT_VALUE)
current->expect = EXPECT_OBJECT_KEY;
else
assert(current->expect == EXPECT_ARRAY_ELEMENT);
break;
}
case _JSON_BUILD_HEX: {
const void *p;
size_t n;
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
r = -EINVAL;
goto finish;
}
p = va_arg(ap, const void *);
n = va_arg(ap, size_t);
if (current->n_suppress == 0) {
r = json_variant_new_hex(&add, p, n);
r = command == _JSON_BUILD_BASE64 ? json_variant_new_base64(&add, p, n) :
command == _JSON_BUILD_BASE32HEX ? json_variant_new_base32hex(&add, p, n) :
command == _JSON_BUILD_HEX ? json_variant_new_hex(&add, p, n) :
json_variant_new_octescape(&add, p, n);
if (r < 0)
goto finish;
}
@@ -4208,6 +4211,19 @@ int json_log_internal(
NULL);
}
static void *dispatch_userdata(const JsonDispatch *p, void *userdata) {
/* When the the userdata pointer is passed in as NULL, then we'll just use the offset as a literal
* address, and convert it to a pointer. Note that might as well just add the offset to the NULL
* pointer, but UndefinedBehaviourSanitizer doesn't like pointer arithmetics based on NULL pointers,
* hence we code this explicitly here. */
if (userdata)
return (uint8_t*) userdata + p->offset;
return SIZE_TO_PTR(p->offset);
}
int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) {
size_t m;
int r, done = 0;
@@ -4271,7 +4287,7 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba
found[p-table] = true;
if (p->callback) {
r = p->callback(json_variant_string(key), value, merged_flags, (uint8_t*) userdata + p->offset);
r = p->callback(json_variant_string(key), value, merged_flags, dispatch_userdata(p, userdata));
if (r < 0) {
if (merged_flags & JSON_PERMISSIVE)
continue;
@@ -4404,6 +4420,36 @@ int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlag
return 0;
}
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
int16_t *i = ASSERT_PTR(userdata);
assert(variant);
if (!json_variant_is_integer(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
if (json_variant_integer(variant) < INT16_MIN || json_variant_integer(variant) > INT16_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
*i = (int16_t) json_variant_integer(variant);
return 0;
}
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
uint16_t *i = ASSERT_PTR(userdata);
assert(variant);
if (!json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name));
if (json_variant_unsigned(variant) > UINT16_MAX)
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name));
*i = (uint16_t) json_variant_unsigned(variant);
return 0;
}
int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
char **s = ASSERT_PTR(userdata);
int r;
+8
View File
@@ -62,7 +62,9 @@ typedef enum JsonVariantType {
int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n);
int json_variant_new_base64(JsonVariant **ret, const void *p, size_t n);
int json_variant_new_base32hex(JsonVariant **ret, const void *p, size_t n);
int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n);
int json_variant_new_octescape(JsonVariant **ret, const void *p, size_t n);
int json_variant_new_integer(JsonVariant **ret, int64_t i);
int json_variant_new_unsigned(JsonVariant **ret, uint64_t u);
int json_variant_new_real(JsonVariant **ret, double d);
@@ -245,7 +247,9 @@ enum {
_JSON_BUILD_LITERAL,
_JSON_BUILD_STRV,
_JSON_BUILD_BASE64,
_JSON_BUILD_BASE32HEX,
_JSON_BUILD_HEX,
_JSON_BUILD_OCTESCAPE,
_JSON_BUILD_ID128,
_JSON_BUILD_BYTE_ARRAY,
_JSON_BUILD_HW_ADDR,
@@ -280,7 +284,9 @@ enum {
#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l }
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l }
#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_BASE32HEX(p, n) _JSON_BUILD_BASE32HEX, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n }
#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_CONST_STRING(s) _JSON_BUILD_VARIANT, JSON_VARIANT_STRING_CONST(s)
@@ -369,6 +375,8 @@ int json_dispatch_int64(const char *name, JsonVariant *variant, JsonDispatchFlag
int json_dispatch_uint64(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_int16(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+3
View File
@@ -68,6 +68,8 @@ enum {
SD_EVENT_PRIORITY_IDLE = 100
};
#define SD_EVENT_SIGNAL_PROCMASK (1 << 30)
typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
@@ -114,6 +116,7 @@ int sd_event_get_exit_code(sd_event *e, int *code);
int sd_event_set_watchdog(sd_event *e, int b);
int sd_event_get_watchdog(sd_event *e);
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
int sd_event_set_signal_exit(sd_event *e, int b);
sd_event_source* sd_event_source_ref(sd_event_source *s);
sd_event_source* sd_event_source_unref(sd_event_source *s);
+1
View File
@@ -99,6 +99,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN);
dump_glyph(SPECIAL_GLYPH_CIRCLE_ARROW);
dump_glyph(SPECIAL_GLYPH_BULLET);
dump_glyph(SPECIAL_GLYPH_ARROW_LEFT);
dump_glyph(SPECIAL_GLYPH_ARROW_RIGHT);
dump_glyph(SPECIAL_GLYPH_ARROW_UP);
dump_glyph(SPECIAL_GLYPH_ARROW_DOWN);