mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
resolved: beef up monitor protocol, include full query info
This commit is contained in:
parent
4d593fb151
commit
72c2d39ecb
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user