aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2022-09-28 12:46:09 +0200
committerLennart Poettering <lennart@poettering.net>2022-09-30 14:23:30 +0200
commit72c2d39ecb2fcd4d6c78b65c56b7a9eab02a3048 (patch)
tree587446694d660b5d3a780a528976a383d4e07319
parentresolved: add dns_question_merge() helper (diff)
downloadsystemd-72c2d39ecb2fcd4d6c78b65c56b7a9eab02a3048.tar.xz
systemd-72c2d39ecb2fcd4d6c78b65c56b7a9eab02a3048.zip
resolved: beef up monitor protocol, include full query info
-rw-r--r--src/resolve/resolved-dns-query.c32
-rw-r--r--src/resolve/resolved-dns-query.h5
-rw-r--r--src/resolve/resolved-manager.c118
-rw-r--r--src/resolve/resolved-manager.h2
-rw-r--r--src/resolve/resolved-varlink.c7
5 files changed, 121 insertions, 43 deletions
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index edd62fa0687..58a7b2d878d 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -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) manager_monitor_send(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);
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 43a833a08a2..2723299bee5 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -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;
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index df3a3fff047..ce5935dc7af 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -1043,62 +1043,100 @@ static int manager_ipv6_send(
return sendmsg_loop(fd, &mh, 0);
}
-int manager_monitor_send(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;
-
- 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;
- }
+ /* 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");
+
+ /* 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_debug_errno(r, "Failed to append notification entry to array: %m");
- }
+ return log_error_errno(r, "Failed to convert answer resource record to JSON: %m");
- if (json_variant_is_blank_object(array))
- return 0;
+ 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 = dns_name_normalize(query_name, 0, &normalized);
- if (r < 0)
- return log_debug_errno(r, "Failed to normalize query name: %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");
+ }
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;
}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 844405c2529..98d90e05b38 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -167,7 +167,7 @@ int manager_start(Manager *m);
uint32_t manager_find_mtu(Manager *m);
-int manager_monitor_send(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);
diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c
index cde406f40e6..e344cf6dd6b 100644
--- a/src/resolve/resolved-varlink.c
+++ b/src/resolve/resolved-varlink.c
@@ -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");