From c7dfa1df8b35f71615f257f177a6d07ddb674145 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 11:28:34 +0200 Subject: [PATCH 01/10] in-addr-util: add byte accessor array to union in_addr_union It's pretty useful to be able to access the bytes generically, without acknowledging a specific family, hence let's a third way to access an in_addr_union. --- src/basic/in-addr-util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index dc3f575bc9..03583f2566 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -12,6 +12,7 @@ union in_addr_union { struct in_addr in; struct in6_addr in6; + uint8_t bytes[CONST_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; }; struct in_addr_data { From 7466e94f13ae690be939a18eb1604f6f0ee8100a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 13:45:25 +0200 Subject: [PATCH 02/10] varlink: add helper for generating errno errors --- src/shared/varlink.c | 9 ++++++++- src/shared/varlink.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/shared/varlink.c b/src/shared/varlink.c index be3559dc10..e2b4bb623d 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -862,7 +862,7 @@ static int varlink_dispatch_method(Varlink *v) { /* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */ if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) { - r = varlink_errorb(v, VARLINK_ERROR_SYSTEM, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("errno", JSON_BUILD_INTEGER(-r)))); + r = varlink_error_errno(v, r); if (r < 0) return r; } @@ -1659,6 +1659,13 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) { return -EINVAL; } +int varlink_error_errno(Varlink *v, int error) { + return varlink_errorb( + v, + VARLINK_ERROR_SYSTEM, + JSON_BUILD_OBJECT(JSON_BUILD_PAIR("errno", JSON_BUILD_INTEGER(abs(error))))); +} + int varlink_notify(Varlink *v, JsonVariant *parameters) { _cleanup_(json_variant_unrefp) JsonVariant *m = NULL; int r; diff --git a/src/shared/varlink.h b/src/shared/varlink.h index 7440f2ca44..06a34b480d 100644 --- a/src/shared/varlink.h +++ b/src/shared/varlink.h @@ -100,6 +100,7 @@ int varlink_replyb(Varlink *v, ...); int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters); int varlink_errorb(Varlink *v, const char *error_id, ...); int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters); +int varlink_error_errno(Varlink *v, int error); /* Enqueue a "more" reply */ int varlink_notify(Varlink *v, JsonVariant *parameters); From 0710343ce79bd06ce0d13be2a156afb6131ec6be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 14:34:38 +0200 Subject: [PATCH 03/10] json: add support for byte arrays to json builder --- src/shared/json.c | 30 ++++++++++++++++++++++++++++++ src/shared/json.h | 2 ++ 2 files changed, 32 insertions(+) diff --git a/src/shared/json.c b/src/shared/json.c index 2e151578ff..04c721e157 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -3624,6 +3624,36 @@ int json_buildv(JsonVariant **ret, va_list ap) { break; } + case _JSON_BUILD_BYTE_ARRAY: { + const void *array; + size_t n; + + if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { + r = -EINVAL; + goto finish; + } + + array = va_arg(ap, const void*); + n = va_arg(ap, size_t); + + if (current->n_suppress == 0) { + r = json_variant_new_array_bytes(&add, array, 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_OBJECT_BEGIN: if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { diff --git a/src/shared/json.h b/src/shared/json.h index ae71593d7b..5fe363f507 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -228,6 +228,7 @@ enum { _JSON_BUILD_STRV, _JSON_BUILD_BASE64, _JSON_BUILD_ID128, + _JSON_BUILD_BYTE_ARRAY, _JSON_BUILD_MAX, }; @@ -249,6 +250,7 @@ enum { #define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; }) #define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; }) #define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, ({ sd_id128_t _x = id; _x; }) +#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, ({ const void *_x = v; _x; }), ({ size_t _y = n; _y; }) int json_build(JsonVariant **ret, ...); int json_buildv(JsonVariant **ret, va_list ap); From b97e3e3d7a203c5a331841386cb6955e36000991 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Aug 2020 08:53:05 +0200 Subject: [PATCH 04/10] json: also add explicit dispatchers for 'int' and 'unsigned' --- src/shared/json.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/shared/json.h b/src/shared/json.h index 5fe363f507..1d6fec67b6 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -304,6 +304,12 @@ assert_cc(sizeof(uintmax_t) == sizeof(uint64_t)); assert_cc(sizeof(intmax_t) == sizeof(int64_t)); #define json_dispatch_int64 json_dispatch_integer +assert_cc(sizeof(uint32_t) == sizeof(unsigned)); +#define json_dispatch_uint json_dispatch_uint32 + +assert_cc(sizeof(int32_t) == sizeof(int)); +#define json_dispatch_int json_dispatch_int32 + static inline int json_dispatch_level(JsonDispatchFlags flags) { /* Did the user request no logging? If so, then never log higher than LOG_DEBUG. Also, if this is marked as From d4f72f0d212b2173811919119ba8a8e58487d6bb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 13:35:14 +0200 Subject: [PATCH 05/10] resolved: drop suppress_unroutable_family field It's unused since 90bdc8be66765df09bbc355783cee7204a5ebb31. --- src/resolve/resolved-bus.c | 1 - src/resolve/resolved-dns-query.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index dba1639a11..0b09dfca58 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -353,7 +353,6 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, q->request = sd_bus_message_ref(message); q->request_family = family; q->complete = bus_method_resolve_hostname_complete; - q->suppress_unroutable_family = family == AF_UNSPEC; r = dns_query_bus_track(q, message); if (r < 0) diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index fe8a219557..e05015fcfb 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -48,10 +48,6 @@ struct DnsQuery { uint64_t flags; int ifindex; - /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address - * family */ - bool suppress_unroutable_family; - /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */ bool clamp_ttl; From c9de4e0f5b87be8d0c447781f091ebeae0d0d5e4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 11:29:55 +0200 Subject: [PATCH 06/10] =?UTF-8?q?resolved:=20rename=20request=20=E2=86=92?= =?UTF-8?q?=20bus=5Frequest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's prepare for adding a new varlink interface, and thus rename the "request" field to "bus_request", so that we can later add a varlink_request field too. --- src/resolve/resolved-bus.c | 72 ++++++++++++++++---------------- src/resolve/resolved-dns-query.c | 2 +- src/resolve/resolved-dns-query.h | 2 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 0b09dfca58..1f4c8b95ea 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -29,40 +29,40 @@ static int reply_query_state(DnsQuery *q) { switch (q->state) { case DNS_TRANSACTION_NO_SERVERS: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); case DNS_TRANSACTION_TIMEOUT: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); + return sd_bus_reply_method_errorf(q->bus_request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); + return sd_bus_reply_method_errorf(q->bus_request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); case DNS_TRANSACTION_INVALID_REPLY: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); case DNS_TRANSACTION_ERRNO: - return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m"); + return sd_bus_reply_method_errnof(q->bus_request, q->answer_errno, "Lookup failed due to system error: %m"); case DNS_TRANSACTION_ABORTED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_ABORTED, "Query aborted"); case DNS_TRANSACTION_DNSSEC_FAILED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", dnssec_result_to_string(q->answer_dnssec_result)); case DNS_TRANSACTION_NO_TRUST_ANCHOR: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known"); case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type"); case DNS_TRANSACTION_NETWORK_DOWN: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down"); + return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NETWORK_DOWN, "Network is down"); case DNS_TRANSACTION_NOT_FOUND: /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ - return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); + return sd_bus_reply_method_errorf(q->bus_request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); case DNS_TRANSACTION_RCODE_FAILURE: { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -83,7 +83,7 @@ static int reply_query_state(DnsQuery *q) { sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc); } - return sd_bus_reply_method_error(q->request, &error); + return sd_bus_reply_method_error(q->bus_request, &error); } case DNS_TRANSACTION_NULL: @@ -152,7 +152,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -160,7 +160,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ return; - r = sd_bus_message_new_method_return(q->request, &reply); + r = sd_bus_message_new_method_return(q->bus_request, &reply); if (r < 0) goto finish; @@ -190,7 +190,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -218,7 +218,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send hostname reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); + sd_bus_reply_method_errno(q->bus_request, r, NULL); } dns_query_free(q); @@ -350,7 +350,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (r < 0) return r; - q->request = sd_bus_message_ref(message); + q->bus_request = sd_bus_message_ref(message); q->request_family = family; q->complete = bus_method_resolve_hostname_complete; @@ -385,7 +385,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -393,7 +393,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ return; - r = sd_bus_message_new_method_return(q->request, &reply); + r = sd_bus_message_new_method_return(q->bus_request, &reply); if (r < 0) goto finish; @@ -427,7 +427,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { _cleanup_free_ char *ip = NULL; (void) in_addr_to_string(q->request_family, &q->request_address, &ip); - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strnull(ip)); goto finish; } @@ -445,7 +445,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send address reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); + sd_bus_reply_method_errno(q->bus_request, r, NULL); } dns_query_free(q); @@ -489,7 +489,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - q->request = sd_bus_message_ref(message); + q->bus_request = sd_bus_message_ref(message); q->request_family = family; q->request_address = a; q->complete = bus_method_resolve_address_complete; @@ -554,7 +554,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -562,7 +562,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ return; - r = sd_bus_message_new_method_return(q->request, &reply); + r = sd_bus_message_new_method_return(q->bus_request, &reply); if (r < 0) goto finish; @@ -587,7 +587,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -604,7 +604,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send record reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); + sd_bus_reply_method_errno(q->bus_request, r, NULL); } dns_query_free(q); @@ -666,7 +666,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd * blob */ q->clamp_ttl = true; - q->request = sd_bus_message_ref(message); + q->bus_request = sd_bus_message_ref(message); q->complete = bus_method_resolve_record_complete; r = dns_query_bus_track(q, message); @@ -890,7 +890,7 @@ static void resolve_service_all_complete(DnsQuery *q) { assert(bad->auxiliary_result != 0); if (bad->auxiliary_result == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad)); goto finish; } @@ -903,7 +903,7 @@ static void resolve_service_all_complete(DnsQuery *q) { } } - r = sd_bus_message_new_method_return(q->request, &reply); + r = sd_bus_message_new_method_return(q->bus_request, &reply); if (r < 0) goto finish; @@ -932,7 +932,7 @@ static void resolve_service_all_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -978,7 +978,7 @@ static void resolve_service_all_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send service reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); + sd_bus_reply_method_errno(q->bus_request, r, NULL); } dns_query_free(q); @@ -1070,7 +1070,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { r = dns_query_process_cname(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -1114,12 +1114,12 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { * domain. Report this as a recognizable * error. See RFC 2782, Section "Usage * Rules". */ - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q)); goto finish; } if (found <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -1130,7 +1130,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send service reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); + sd_bus_reply_method_errno(q->bus_request, r, NULL); } dns_query_free(q); @@ -1192,7 +1192,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (r < 0) return r; - q->request = sd_bus_message_ref(message); + q->bus_request = sd_bus_message_ref(message); q->request_family = family; q->complete = bus_method_resolve_service_complete; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 87c1ced857..cd943ab843 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -338,7 +338,7 @@ DnsQuery *dns_query_free(DnsQuery *q) { dns_query_reset_answer(q); - sd_bus_message_unref(q->request); + sd_bus_message_unref(q->bus_request); sd_bus_track_unref(q->bus_track); dns_packet_unref(q->request_dns_packet); diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index e05015fcfb..5dd439c193 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -69,7 +69,7 @@ struct DnsQuery { bool previous_redirect_unauthenticated; /* Bus client information */ - sd_bus_message *request; + sd_bus_message *bus_request; int request_family; bool request_address_valid; union in_addr_union request_address; From 65a01e82423dbffc45e897dabfd284419eb463c0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 13:43:51 +0200 Subject: [PATCH 07/10] resolved: move query bus tracking to resolved-bus.c It's strictly bus-specific, hence let's move this to resolved-bus.c like the rest of the bus specific logic. This is also in preparation for adding an alternative varlink transport, which needs similar functionality, but varlink instead of bus-specific. --- src/resolve/resolved-bus.c | 37 ++++++++++++++++++++++++++++++-- src/resolve/resolved-dns-query.c | 37 +++----------------------------- src/resolve/resolved-dns-query.h | 2 +- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 1f4c8b95ea..4a36c554a9 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -24,6 +24,39 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_resolve_support, resolve_support, ResolveSupport); +static int query_on_bus_track(sd_bus_track *t, void *userdata) { + DnsQuery *q = userdata; + + assert(t); + assert(q); + + if (!DNS_TRANSACTION_IS_LIVE(q->state)) + return 0; + + log_debug("Client of active query vanished, aborting query."); + dns_query_complete(q, DNS_TRANSACTION_ABORTED); + return 0; +} + +static int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { + int r; + + assert(q); + assert(m); + + if (!q->bus_track) { + r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, query_on_bus_track, q); + if (r < 0) + return r; + } + + r = sd_bus_track_add_sender(q->bus_track, m); + if (r < 0) + return r; + + return 0; +} + static int reply_query_state(DnsQuery *q) { switch (q->state) { @@ -1689,7 +1722,7 @@ static int bus_method_reset_server_features(sd_bus_message *message, void *userd return sd_bus_reply_method_return(message, NULL); } -static int on_bus_track(sd_bus_track *t, void *userdata) { +static int dnssd_service_on_bus_track(sd_bus_track *t, void *userdata) { DnssdService *s = userdata; assert(t); @@ -1862,7 +1895,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata, if (r < 0) return r; - r = sd_bus_track_new(sd_bus_message_get_bus(message), &bus_track, on_bus_track, service); + r = sd_bus_track_new(sd_bus_message_get_bus(message), &bus_track, dnssd_service_on_bus_track, service); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index cd943ab843..ec57474351 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -473,14 +473,13 @@ int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) { return 0; } -static void dns_query_complete(DnsQuery *q, DnsTransactionState state) { +void dns_query_complete(DnsQuery *q, DnsTransactionState state) { assert(q); assert(!DNS_TRANSACTION_IS_LIVE(state)); assert(DNS_TRANSACTION_IS_LIVE(q->state)); - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function. */ + /* Note that this call might invalidate the query. Callers should hence not attempt to access the + * query or transaction after calling this function. */ q->state = state; @@ -987,36 +986,6 @@ int dns_query_process_cname(DnsQuery *q) { return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */ } -static int on_bus_track(sd_bus_track *t, void *userdata) { - DnsQuery *q = userdata; - - assert(t); - assert(q); - - log_debug("Client of active query vanished, aborting query."); - dns_query_complete(q, DNS_TRANSACTION_ABORTED); - return 0; -} - -int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { - int r; - - assert(q); - assert(m); - - if (!q->bus_track) { - r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(q->bus_track, m); - if (r < 0) - return r; - - return 0; -} - DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) { assert(q); diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 5dd439c193..d48cc8eb6d 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -112,7 +112,7 @@ void dns_query_ready(DnsQuery *q); int dns_query_process_cname(DnsQuery *q); -int dns_query_bus_track(DnsQuery *q, sd_bus_message *m); +void dns_query_complete(DnsQuery *q, DnsTransactionState state); DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol); From c17b5ce7390fada5c95e98fa491bd4fe9f71d102 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 14:36:20 +0200 Subject: [PATCH 08/10] resolved: minor clean-ups for resolved-bus.c --- src/resolve/resolved-bus.c | 40 +++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 4a36c554a9..97cc125232 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -59,6 +59,9 @@ static int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { static int reply_query_state(DnsQuery *q) { + assert(q); + assert(q->bus_request); + switch (q->state) { case DNS_TRANSACTION_NO_SERVERS: @@ -172,6 +175,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *normalized = NULL; + DnsQuestion *question; DnsResourceRecord *rr; unsigned added = 0; int ifindex, r; @@ -201,10 +205,9 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - DnsQuestion *question; + question = dns_query_question_for_protocol(q, q->answer_protocol); - question = dns_query_question_for_protocol(q, q->answer_protocol); + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) @@ -231,14 +234,14 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; - /* The key names are not necessarily normalized, make sure that they are when we return them to our bus - * clients. */ + /* The key names are not necessarily normalized, make sure that they are when we return them to our + * bus clients. */ + assert(canonical); r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); if (r < 0) goto finish; /* Return the precise spelling and uppercasing and CNAME target reported by the server */ - assert(canonical); r = sd_bus_message_append( reply, "st", normalized, @@ -257,9 +260,23 @@ finish: dns_query_free(q); } -static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) { +static int validate_and_mangle_ifindex_and_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) { assert(flags); + /* Checks that the client supplied interface index and flags parameter actually are valid and make + * sense in our method call context. Specifically: + * + * 1. Checks that the interface index is either 0 (meaning *all* interfaces) or positive + * + * 2. Only the protocols flags and the NO_CNAME flag are set, at most. Plus additional flags specific + * to our method, passed in the "ok" parameter. + * + * 3. If zero protocol flags are specified it is automatically turned into *all* protocols. This way + * clients can simply pass 0 as flags and all will work as it should. They can also use this so + * that clients don't have to know all the protocols resolved implements, but can just specify 0 + * to mean "all supported protocols". + */ + if (ifindex < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); @@ -357,7 +374,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); + r = validate_and_mangle_ifindex_and_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); if (r < 0) return r; @@ -510,7 +527,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = check_ifindex_flags(ifindex, &flags, 0, error); + r = validate_and_mangle_ifindex_and_flags(ifindex, &flags, 0, error); if (r < 0) return r; @@ -675,7 +692,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (dns_type_is_obsolete(type)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type); - r = check_ifindex_flags(ifindex, &flags, 0, error); + r = validate_and_mangle_ifindex_and_flags(ifindex, &flags, 0, error); if (r < 0) return r; @@ -945,6 +962,7 @@ static void resolve_service_all_complete(DnsQuery *q) { goto finish; question = dns_query_question_for_protocol(q, q->answer_protocol); + DNS_ANSWER_FOREACH(rr, q->answer) { r = dns_question_matches_rr(question, rr, NULL); if (r < 0) @@ -1209,7 +1227,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s if (name && !type) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type."); - r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error); + r = validate_and_mangle_ifindex_and_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error); if (r < 0) return r; From 9581bb842470ac4dee7d230e5beef5d3003ab105 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 13:45:55 +0200 Subject: [PATCH 09/10] resolved: add minimal varlink api for resolving hostnames/addresses This allows us to later port nss-resolve to use Varlink rather than D-Bus for resolution. This has the benefit that nss-resolve based resoluton works even without D-Bus being up. And it's faster too. --- src/resolve/meson.build | 84 ++--- src/resolve/resolved-dns-query.c | 5 + src/resolve/resolved-dns-query.h | 4 +- src/resolve/resolved-manager.c | 6 + src/resolve/resolved-manager.h | 3 + src/resolve/resolved-varlink.c | 534 +++++++++++++++++++++++++++++++ src/resolve/resolved-varlink.h | 7 + 7 files changed, 601 insertions(+), 42 deletions(-) create mode 100644 src/resolve/resolved-varlink.c create mode 100644 src/resolve/resolved-varlink.h diff --git a/src/resolve/meson.build b/src/resolve/meson.build index 92b67b6333..da2256f5cc 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -17,53 +17,55 @@ basic_dns_sources = files(''' dns_type_h = files('dns-type.h')[0] systemd_resolved_sources = files(''' - resolved.c - resolved-manager.c - resolved-manager.h - resolved-dnssd.c - resolved-dnssd.h - resolved-dnssd-bus.c - resolved-dnssd-bus.h - resolved-conf.c - resolved-conf.h - resolved-resolv-conf.c - resolved-resolv-conf.h resolved-bus.c resolved-bus.h - resolved-link.h - resolved-link.c + resolved-conf.c + resolved-conf.h + resolved-def.h + resolved-dns-cache.c + resolved-dns-cache.h + resolved-dns-query.c + resolved-dns-query.h + resolved-dns-scope.c + resolved-dns-scope.h + resolved-dns-search-domain.c + resolved-dns-search-domain.h + resolved-dns-server.c + resolved-dns-server.h + resolved-dns-stream.c + resolved-dns-stream.h + resolved-dns-stub.c + resolved-dns-stub.h + resolved-dns-synthesize.c + resolved-dns-synthesize.h + resolved-dns-transaction.c + resolved-dns-transaction.h + resolved-dns-trust-anchor.c + resolved-dns-trust-anchor.h + resolved-dns-zone.c + resolved-dns-zone.h + resolved-dnssd-bus.c + resolved-dnssd-bus.h + resolved-dnssd.c + resolved-dnssd.h + resolved-dnstls.h + resolved-etc-hosts.c + resolved-etc-hosts.h resolved-link-bus.c resolved-link-bus.h - resolved-llmnr.h + resolved-link.c + resolved-link.h resolved-llmnr.c - resolved-mdns.h + resolved-llmnr.h + resolved-manager.c + resolved-manager.h resolved-mdns.c - resolved-def.h - resolved-dns-query.h - resolved-dns-query.c - resolved-dns-synthesize.h - resolved-dns-synthesize.c - resolved-dns-transaction.h - resolved-dns-transaction.c - resolved-dns-scope.h - resolved-dns-scope.c - resolved-dns-server.h - resolved-dns-server.c - resolved-dns-search-domain.h - resolved-dns-search-domain.c - resolved-dns-cache.h - resolved-dns-cache.c - resolved-dns-zone.h - resolved-dns-zone.c - resolved-dns-stream.h - resolved-dns-stream.c - resolved-dns-trust-anchor.h - resolved-dns-trust-anchor.c - resolved-dns-stub.h - resolved-dns-stub.c - resolved-etc-hosts.h - resolved-etc-hosts.c - resolved-dnstls.h + resolved-mdns.h + resolved-resolv-conf.c + resolved-resolv-conf.h + resolved-varlink.c + resolved-varlink.h + resolved.c '''.split()) resolvectl_sources = files(''' diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index ec57474351..894b98834d 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -341,6 +341,11 @@ DnsQuery *dns_query_free(DnsQuery *q) { sd_bus_message_unref(q->bus_request); sd_bus_track_unref(q->bus_track); + if (q->varlink_request) { + varlink_set_userdata(q->varlink_request, NULL); + varlink_unref(q->varlink_request); + } + dns_packet_unref(q->request_dns_packet); dns_packet_unref(q->reply_dns_packet); diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index d48cc8eb6d..95e5f5bbeb 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -4,6 +4,7 @@ #include "sd-bus.h" #include "set.h" +#include "varlink.h" typedef struct DnsQueryCandidate DnsQueryCandidate; typedef struct DnsQuery DnsQuery; @@ -68,8 +69,9 @@ struct DnsQuery { int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ bool previous_redirect_unauthenticated; - /* Bus client information */ + /* Bus + Varlink client information */ sd_bus_message *bus_request; + Varlink *varlink_request; int request_family; bool request_address_valid; union in_addr_union request_address; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 1da590b68a..17bdb67ed5 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -36,6 +36,7 @@ #include "resolved-manager.h" #include "resolved-mdns.h" #include "resolved-resolv-conf.h" +#include "resolved-varlink.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -663,6 +664,10 @@ int manager_start(Manager *m) { if (r < 0) return r; + r = manager_varlink_init(m); + if (r < 0) + return r; + return 0; } @@ -706,6 +711,7 @@ Manager *manager_free(Manager *m) { manager_llmnr_stop(m); manager_mdns_stop(m); manager_dns_stub_stop(m); + manager_varlink_done(m); bus_verify_polkit_async_registry_free(m->polkit_registry); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 59944df746..390fac4a52 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -11,6 +11,7 @@ #include "list.h" #include "ordered-set.h" #include "resolve-util.h" +#include "varlink.h" typedef struct Manager Manager; @@ -140,6 +141,8 @@ struct Manager { sd_event_source *dns_stub_tcp_event_source; Hashmap *polkit_registry; + + VarlinkServer *varlink_server; }; /* Manager */ diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c new file mode 100644 index 0000000000..2a65293c2a --- /dev/null +++ b/src/resolve/resolved-varlink.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "in-addr-util.h" +#include "resolved-dns-synthesize.h" +#include "resolved-varlink.h" +#include "socket-netlink.h" + +typedef struct LookupParameters { + int ifindex; + uint64_t flags; + int family; + union in_addr_union address; + size_t address_size; + char *name; +} LookupParameters; + +static void lookup_parameters_destroy(LookupParameters *p) { + assert(p); + free(p->name); +} + +static int reply_query_state(DnsQuery *q) { + + assert(q); + assert(q->varlink_request); + + switch (q->state) { + + case DNS_TRANSACTION_NO_SERVERS: + return varlink_error(q->varlink_request, "io.systemd.Resolve.NoNameServers", NULL); + + case DNS_TRANSACTION_TIMEOUT: + return varlink_error(q->varlink_request, "io.systemd.Resolve.QueryTimedOut", NULL); + + case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: + return varlink_error(q->varlink_request, "io.systemd.Resolve.MaxAttemptsReached", NULL); + + case DNS_TRANSACTION_INVALID_REPLY: + return varlink_error(q->varlink_request, "io.systemd.Resolve.InvalidReply", NULL); + + case DNS_TRANSACTION_ERRNO: + return varlink_error_errno(q->varlink_request, q->answer_errno); + + case DNS_TRANSACTION_ABORTED: + return varlink_error(q->varlink_request, "io.systemd.Resolve.QueryAborted", NULL); + + case DNS_TRANSACTION_DNSSEC_FAILED: + return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSSECValidationFailed", + JSON_BUILD_OBJECT(JSON_BUILD_PAIR("result", JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))))); + + case DNS_TRANSACTION_NO_TRUST_ANCHOR: + return varlink_error(q->varlink_request, "io.systemd.Resolve.NoTrustAnchor", NULL); + + case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: + return varlink_error(q->varlink_request, "io.systemd.Resolve.ResourceRecordTypeUnsupported", NULL); + + case DNS_TRANSACTION_NETWORK_DOWN: + return varlink_error(q->varlink_request, "io.systemd.Resolve.NetworkDown", NULL); + + case DNS_TRANSACTION_NOT_FOUND: + /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we + * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ + return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSError", + JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(DNS_RCODE_NXDOMAIN)))); + + case DNS_TRANSACTION_RCODE_FAILURE: + return varlink_errorb(q->varlink_request, "io.systemd.Resolve.DNSError", + JSON_BUILD_OBJECT(JSON_BUILD_PAIR("rcode", JSON_BUILD_INTEGER(q->answer_rcode)))); + + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_VALIDATING: + case DNS_TRANSACTION_SUCCESS: + default: + assert_not_reached("Impossible state"); + } +} + +static void vl_on_disconnect(VarlinkServer *s, Varlink *link, void *userdata) { + DnsQuery *q; + + assert(s); + assert(link); + + q = varlink_get_userdata(link); + if (!q) + return; + + if (!DNS_TRANSACTION_IS_LIVE(q->state)) + return; + + log_debug("Client of active query vanished, aborting query."); + dns_query_complete(q, DNS_TRANSACTION_ABORTED); +} + +static bool validate_and_mangle_flags(uint64_t *flags, uint64_t ok) { + assert(flags); + + /* This checks that the specified client-provided flags parameter actually makes sense, and mangles + * it slightly. Specifically: + * + * 1. We check that only the protocol flags and the NO_CNAME flag are on at most, plus the + * method-specific flags specified in 'ok'. + * + * 2. If no protocols are enabled we automatically convert that to "all protocols are enabled". + * + * The second rule means that clients can just pass 0 as flags for the common case, and all supported + * protocols are enabled. Moreover it's useful so that client's do not have to be aware of all + * protocols implemented in resolved, but can use 0 as protocols flags set as indicator for + * "everything". + */ + + if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok)) + return false; + + if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */ + *flags |= SD_RESOLVED_PROTOCOLS_ALL; + + return true; +} + +static void vl_method_resolve_hostname_complete(DnsQuery *q) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + _cleanup_free_ char *normalized = NULL; + DnsResourceRecord *rr; + DnsQuestion *question; + int ifindex, r; + + assert(q); + + if (q->state != DNS_TRANSACTION_SUCCESS) { + r = reply_query_state(q); + goto finish; + } + + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL); + goto finish; + } + if (r < 0) + goto finish; + if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ + return; + + question = dns_query_question_for_protocol(q, q->answer_protocol); + + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL; + int family; + const void *p; + + r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + if (r < 0) + goto finish; + if (r == 0) + continue; + + if (rr->key->type == DNS_TYPE_A) { + family = AF_INET; + p = &rr->a.in_addr; + } else if (rr->key->type == DNS_TYPE_AAAA) { + family = AF_INET6; + p = &rr->aaaa.in6_addr; + } else { + r = -EAFNOSUPPORT; + goto finish; + } + + r = json_build(&entry, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("ifindex", JSON_BUILD_INTEGER(ifindex)), + JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(family)), + JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(p, FAMILY_ADDRESS_SIZE(family))))); + if (r < 0) + goto finish; + + if (!canonical) + canonical = dns_resource_record_ref(rr); + + r = json_variant_append_array(&array, entry); + if (r < 0) + goto finish; + } + + if (json_variant_is_blank_object(array)) { + r = varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL); + goto finish; + } + + assert(canonical); + r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); + if (r < 0) + goto finish; + + r = varlink_replyb(q->varlink_request, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("addresses", JSON_BUILD_VARIANT(array)), + JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized)), + JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)))))); +finish: + if (r < 0) { + log_error_errno(r, "Failed to send hostname reply: %m"); + r = varlink_error_errno(q->varlink_request, r); + } + + dns_query_free(q); +} + +static int parse_as_address(Varlink *link, LookupParameters *p) { + _cleanup_free_ char *canonical = NULL; + int r, ff, parsed_ifindex, ifindex; + union in_addr_union parsed; + + assert(link); + assert(p); + + /* Check if this parses as literal address. If so, just parse it and return that, do not involve networking */ + r = in_addr_ifindex_from_string_auto(p->name, &ff, &parsed, &parsed_ifindex); + if (r < 0) + return 0; /* not a literal address */ + + /* Make sure the data we parsed matches what is requested */ + if ((p->family != AF_UNSPEC && ff != p->family) || + (p->ifindex > 0 && parsed_ifindex > 0 && parsed_ifindex != p->ifindex)) + return varlink_error(link, "io.systemd.Resolve.NoSuchResourceRecord", NULL); + + ifindex = parsed_ifindex > 0 ? parsed_ifindex : p->ifindex; + + /* Reformat the address as string, to return as canonicalized name */ + r = in_addr_ifindex_to_string(ff, &parsed, ifindex, &canonical); + if (r < 0) + return r; + + return varlink_replyb( + link, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("addresses", + JSON_BUILD_ARRAY( + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)), + JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(ff)), + JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(&parsed, FAMILY_ADDRESS_SIZE(ff)))))), + JSON_BUILD_PAIR("name", JSON_BUILD_STRING(canonical)), + JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(p->flags), ff, true))))); +} + +static int vl_method_resolve_hostname(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + static const JsonDispatch dispatch_table[] = { + { "ifindex", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, ifindex), 0 }, + { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(LookupParameters, name), JSON_MANDATORY }, + { "family", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, family), 0 }, + { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, + {} + }; + + _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; + _cleanup_(lookup_parameters_destroy) LookupParameters p = { + .family = AF_UNSPEC, + }; + Manager *m = userdata; + DnsQuery *q; + int r; + + assert(link); + assert(m); + + if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) + return -EINVAL; + + r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); + if (r < 0) + return r; + + if (p.ifindex < 0) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex")); + + r = dns_name_is_valid(p.name); + if (r < 0) + return r; + if (r == 0) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("name")); + + if (!IN_SET(p.family, AF_UNSPEC, AF_INET, AF_INET6)) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); + + if (!validate_and_mangle_flags(&p.flags, SD_RESOLVED_NO_SEARCH)) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); + + r = parse_as_address(link, &p); + if (r != 0) + return r; + + r = dns_question_new_address(&question_utf8, p.family, p.name, false); + if (r < 0) + return r; + + r = dns_question_new_address(&question_idna, p.family, p.name, true); + if (r < 0 && r != -EALREADY) + return r; + + r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, p.ifindex, p.flags); + if (r < 0) + return r; + + q->varlink_request = varlink_ref(link); + varlink_set_userdata(link, q); + q->request_family = p.family; + q->complete = vl_method_resolve_hostname_complete; + + r = dns_query_go(q); + if (r < 0) + goto fail; + + return 1; + +fail: + dns_query_free(q); + return r; +} + +static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + LookupParameters *p = userdata; + union in_addr_union buf = {}; + JsonVariant *i; + size_t n, k = 0; + + assert(variant); + assert(p); + + if (!json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); + + n = json_variant_elements(variant); + if (!IN_SET(n, 4, 16)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name)); + + JSON_VARIANT_ARRAY_FOREACH(i, variant) { + intmax_t b; + + if (!json_variant_is_integer(i)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name)); + + b = json_variant_integer(i); + if (b < 0 || b > 0xff) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is out of range 0…255.", k, strna(name)); + + buf.bytes[k++] = (uint8_t) b; + } + + p->address = buf; + p->address_size = k; + + return 0; +} + +static void vl_method_resolve_address_complete(DnsQuery *q) { + _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; + DnsQuestion *question; + DnsResourceRecord *rr; + int ifindex, r; + + assert(q); + + if (q->state != DNS_TRANSACTION_SUCCESS) { + r = reply_query_state(q); + goto finish; + } + + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL); + goto finish; + } + if (r < 0) + goto finish; + if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */ + return; + + question = dns_query_question_for_protocol(q, q->answer_protocol); + + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + _cleanup_(json_variant_unrefp) JsonVariant *entry = NULL; + _cleanup_free_ char *normalized = NULL; + + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; + + r = dns_name_normalize(rr->ptr.name, 0, &normalized); + if (r < 0) + goto finish; + + r = json_build(&entry, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("ifindex", JSON_BUILD_INTEGER(ifindex)), + JSON_BUILD_PAIR("name", JSON_BUILD_STRING(normalized)))); + if (r < 0) + goto finish; + + r = json_variant_append_array(&array, entry); + if (r < 0) + goto finish; + } + + if (json_variant_is_blank_object(array)) { + r = varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL); + goto finish; + } + + r = varlink_replyb(q->varlink_request, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("names", JSON_BUILD_VARIANT(array)), + JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q)))))); +finish: + if (r < 0) { + log_error_errno(r, "Failed to send address reply: %m"); + r = varlink_error_errno(q->varlink_request, r); + } + + dns_query_free(q); +} + +static int vl_method_resolve_address(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) { + static const JsonDispatch dispatch_table[] = { + { "ifindex", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, ifindex), 0 }, + { "family", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParameters, family), JSON_MANDATORY }, + { "address", JSON_VARIANT_ARRAY, json_dispatch_address, 0, JSON_MANDATORY }, + { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, + {} + }; + + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(lookup_parameters_destroy) LookupParameters p = { + .family = AF_UNSPEC, + }; + Manager *m = userdata; + DnsQuery *q; + int r; + + assert(link); + assert(m); + + if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) + return -EINVAL; + + r = json_dispatch(parameters, dispatch_table, NULL, 0, &p); + if (r < 0) + return r; + + if (p.ifindex < 0) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex")); + + if (!IN_SET(p.family, AF_UNSPEC, AF_INET, AF_INET6)) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); + + if (FAMILY_ADDRESS_SIZE(p.family) != p.address_size) + return varlink_error(link, "io.systemd.UserDatabase.BadAddressSize", NULL); + + if (!validate_and_mangle_flags(&p.flags, 0)) + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); + + r = dns_question_new_reverse(&question, p.family, &p.address); + if (r < 0) + return r; + + r = dns_query_new(m, &q, question, question, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH); + if (r < 0) + return r; + + q->varlink_request = varlink_ref(link); + varlink_set_userdata(link, q); + + q->request_family = p.family; + q->request_address = p.address; + q->complete = vl_method_resolve_address_complete; + + r = dns_query_go(q); + if (r < 0) + goto fail; + + return 1; + +fail: + dns_query_free(q); + return r; +} + +int manager_varlink_init(Manager *m) { + _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL; + int r; + + assert(m); + + if (m->varlink_server) + return 0; + + r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID); + if (r < 0) + return log_error_errno(r, "Failed to allocate varlink server object: %m"); + + varlink_server_set_userdata(s, m); + + r = varlink_server_bind_method_many( + s, + "io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname, + "io.systemd.Resolve.ResolveAddress", vl_method_resolve_address); + if (r < 0) + return log_error_errno(r, "Failed to register varlink methods: %m"); + + r = varlink_server_bind_disconnect(s, vl_on_disconnect); + if (r < 0) + return log_error_errno(r, "Failed to register varlink disconnect handler: %m"); + + r = varlink_server_listen_address(s, "/run/systemd/resolve/io.systemd.Resolve", 0666); + if (r < 0) + return log_error_errno(r, "Failed to bind to varlink socket: %m"); + + r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); + + m->varlink_server = TAKE_PTR(s); + return 0; +} + +void manager_varlink_done(Manager *m) { + assert(m); + + m->varlink_server = varlink_server_unref(m->varlink_server); +} diff --git a/src/resolve/resolved-varlink.h b/src/resolve/resolved-varlink.h new file mode 100644 index 0000000000..cfb90e5e5e --- /dev/null +++ b/src/resolve/resolved-varlink.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "resolved-manager.h" + +int manager_varlink_init(Manager *m); +void manager_varlink_done(Manager *m); From 0c73f4f075a2d23f7cabe708b589f19f4bbbec37 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Aug 2020 19:05:37 +0200 Subject: [PATCH 10/10] nss-resolve: port over to new varlink interface --- src/nss-resolve/nss-resolve.c | 536 ++++++++++++++++++---------------- 1 file changed, 280 insertions(+), 256 deletions(-) diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index a3416c83c8..a8e6ce97a1 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -7,10 +7,6 @@ #include #include -#include "sd-bus.h" - -#include "bus-common-errors.h" -#include "bus-locator.h" #include "errno-util.h" #include "in-addr-util.h" #include "macro.h" @@ -18,69 +14,36 @@ #include "resolved-def.h" #include "signal-util.h" #include "string-util.h" +#include "strv.h" +#include "varlink.h" NSS_GETHOSTBYNAME_PROTOTYPES(resolve); NSS_GETHOSTBYADDR_PROTOTYPES(resolve); -static bool bus_error_shall_fallback(sd_bus_error *e) { - return sd_bus_error_has_names(e, - SD_BUS_ERROR_SERVICE_UNKNOWN, - SD_BUS_ERROR_NAME_HAS_NO_OWNER, - SD_BUS_ERROR_NO_REPLY, - SD_BUS_ERROR_ACCESS_DENIED, - SD_BUS_ERROR_DISCONNECTED, - SD_BUS_ERROR_TIMEOUT, - BUS_ERROR_NO_SUCH_UNIT); +static bool error_shall_fallback(const char *error_id) { + return STR_IN_SET(error_id, + VARLINK_ERROR_DISCONNECTED, + VARLINK_ERROR_TIMEOUT, + VARLINK_ERROR_PROTOCOL, + VARLINK_ERROR_INTERFACE_NOT_FOUND, + VARLINK_ERROR_METHOD_NOT_FOUND, + VARLINK_ERROR_METHOD_NOT_IMPLEMENTED); } -static int count_addresses(sd_bus_message *m, int af, const char **canonical) { - int c = 0, r; +static int connect_to_resolved(Varlink **ret) { + _cleanup_(varlink_unrefp) Varlink *link = NULL; + int r; - assert(m); - assert(canonical); - - r = sd_bus_message_enter_container(m, 'a', "(iiay)"); + r = varlink_connect_address(&link, "/run/systemd/resolve/io.systemd.Resolve"); if (r < 0) return r; - while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { - int family, ifindex; - - assert_cc(sizeof(int32_t) == sizeof(int)); - - r = sd_bus_message_read(m, "ii", &ifindex, &family); - if (r < 0) - return r; - - r = sd_bus_message_skip(m, "ay"); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - if (af != AF_UNSPEC && family != af) - continue; - - c++; - } + r = varlink_set_relative_timeout(link, SD_RESOLVED_QUERY_TIMEOUT_USEC); if (r < 0) return r; - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_read(m, "s", canonical); - if (r < 0) - return r; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - return c; + *ret = TAKE_PTR(link); + return 0; } static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) { @@ -97,20 +60,111 @@ static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) { return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0; } -static bool avoid_deadlock(void) { +static int json_dispatch_ifindex(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + int *ifi = userdata; + intmax_t t; - /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager - * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to - * systemd-resolved if we are required to finish before it can be started. This of course won't detect all - * possible dead locks of this kind, but it should work for the most obvious cases. */ + assert(variant); + assert(ifi); - if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */ - return false; + if (!json_variant_is_integer(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); - return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") && - streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system"); + t = json_variant_integer(variant); + if (t <= 0 || t > INT_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is out of bounds for an interface index.", strna(name)); + + *ifi = (int) t; + return 0; } +static int json_dispatch_family(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + int *family = userdata; + intmax_t t; + + assert(variant); + assert(family); + + if (!json_variant_is_integer(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); + + t = json_variant_integer(variant); + if (t < 0 || t > INT_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid family.", strna(name)); + + *family = (int) t; + return 0; +} + +typedef struct ResolveHostnameReply { + JsonVariant *addresses; + char *name; + uint64_t flags; +} ResolveHostnameReply; + +static void resolve_hostname_reply_destroy(ResolveHostnameReply *p) { + assert(p); + + json_variant_unref(p->addresses); + free(p->name); +} + +static const JsonDispatch resolve_hostname_reply_dispatch_table[] = { + { "addresses", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(ResolveHostnameReply, addresses), JSON_MANDATORY }, + { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ResolveHostnameReply, name), 0 }, + { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(ResolveHostnameReply, flags), 0 }, + {} +}; + +typedef struct AddressParameters { + int ifindex; + int family; + union in_addr_union address; + size_t address_size; +} AddressParameters; + +static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + AddressParameters *p = userdata; + union in_addr_union buf = {}; + JsonVariant *i; + size_t n, k = 0; + + assert(variant); + assert(p); + + if (!json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); + + n = json_variant_elements(variant); + if (!IN_SET(n, 4, 16)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name)); + + JSON_VARIANT_ARRAY_FOREACH(i, variant) { + intmax_t b; + + if (!json_variant_is_integer(i)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name)); + + b = json_variant_integer(i); + if (b < 0 || b > 0xff) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is out of range 0…255.", k, strna(name)); + + buf.bytes[k++] = (uint8_t) b; + } + + p->address = buf; + p->address_size = k; + + return 0; +} + +static const JsonDispatch address_parameters_dispatch_table[] = { + { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(AddressParameters, ifindex), 0 }, + { "family", JSON_VARIANT_INTEGER, json_dispatch_family, offsetof(AddressParameters, family), JSON_MANDATORY }, + { "address", JSON_VARIANT_ARRAY, json_dispatch_address, 0, JSON_MANDATORY }, + {} +}; + enum nss_status _nss_resolve_gethostbyname4_r( const char *name, struct gaih_addrtuple **pat, @@ -118,14 +172,15 @@ enum nss_status _nss_resolve_gethostbyname4_r( int *errnop, int *h_errnop, int32_t *ttlp) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - const char *canonical = NULL; - size_t l, ms, idx; + _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {}; + _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL; + struct gaih_addrtuple *r_tuple = NULL, *r_tuple_first = NULL; + _cleanup_(varlink_unrefp) Varlink *link = NULL; + const char *canonical = NULL, *error_id = NULL; + JsonVariant *entry, *rparams; + size_t l, ms, idx, c = 0; char *r_name; - int c, r, i = 0; + int r; PROTECT_ERRNO; BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); @@ -136,50 +191,53 @@ enum nss_status _nss_resolve_gethostbyname4_r( assert(errnop); assert(h_errnop); - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); + r = connect_to_resolved(&link); if (r < 0) goto fail; - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); + r = json_build(&cparams, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)))); if (r < 0) goto fail; - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL); if (r < 0) { - if (!bus_error_shall_fallback(&error)) + if (!error_shall_fallback(error_id)) goto not_found; - /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, - allowing falling back to other nss modules. Treat all other error conditions as - NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this - case so that the nsswitch.conf configuration can distinguish such executed but - negative replies from complete failure to talk to resolved). */ + /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, allowing falling + back to other nss modules. Treat all other error conditions as NOTFOUND. This includes + DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf + configuration can distinguish such executed but negative replies from complete failure to + talk to resolved). */ goto fail; } - c = count_addresses(reply, AF_UNSPEC, &canonical); - if (c < 0) { - r = c; + r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, 0, &p); + if (r < 0) goto fail; - } - if (c == 0) + if (json_variant_is_blank_object(p.addresses)) goto not_found; - if (isempty(canonical)) - canonical = name; + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + + r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q); + if (r < 0) + goto fail; + + if (!IN_SET(q.family, AF_INET, AF_INET6)) + continue; + + if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) { + r = -EINVAL; + goto fail; + } + + c++; + } + + canonical = p.name ?: name; l = strlen(canonical); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; @@ -198,56 +256,29 @@ enum nss_status _nss_resolve_gethostbyname4_r( /* Second, append addresses */ r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); - if (r < 0) - goto fail; + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { - int family, ifindex; - const void *a; - size_t sz; - - assert_cc(sizeof(int32_t) == sizeof(int)); - - r = sd_bus_message_read(reply, "ii", &ifindex, &family); + r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q); if (r < 0) goto fail; - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (!IN_SET(family, AF_INET, AF_INET6)) + if (!IN_SET(q.family, AF_INET, AF_INET6)) continue; - if (sz != FAMILY_ADDRESS_SIZE(family)) { - r = -EINVAL; - goto fail; - } - r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); + r_tuple->next = (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); r_tuple->name = r_name; - r_tuple->family = family; - r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex); - memcpy(r_tuple->addr, a, sz); + r_tuple->family = q.family; + r_tuple->scopeid = ifindex_to_scopeid(q.family, &q.address, q.ifindex); + memcpy(r_tuple->addr, &q.address, q.address_size); idx += ALIGN(sizeof(struct gaih_addrtuple)); - i++; } - if (r < 0) - goto fail; - assert(i == c); + assert(r_tuple); + r_tuple->next = NULL; /* Override last next pointer */ + assert(idx == ms); if (*pat) @@ -285,13 +316,14 @@ enum nss_status _nss_resolve_gethostbyname3_r( int32_t *ttlp, char **canonp) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {}; + _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - size_t l, idx, ms, alen; - const char *canonical; - int c, r, i = 0; + _cleanup_(varlink_unrefp) Varlink *link = NULL; + const char *canonical, *error_id = NULL; + size_t l, idx, ms, alen, i = 0, c = 0; + JsonVariant *entry, *rparams; + int r; PROTECT_ERRNO; BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); @@ -310,50 +342,53 @@ enum nss_status _nss_resolve_gethostbyname3_r( goto fail; } - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); + r = connect_to_resolved(&link); if (r < 0) goto fail; - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); + r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)), + JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)))); if (r < 0) goto fail; - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL); if (r < 0) { - if (!bus_error_shall_fallback(&error)) + if (!error_shall_fallback(error_id)) goto not_found; goto fail; } - c = count_addresses(reply, af, &canonical); - if (c < 0) { - r = c; + r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, 0, &p); + if (r < 0) goto fail; - } - if (c == 0) + if (json_variant_is_blank_object(p.addresses)) goto not_found; - if (isempty(canonical)) - canonical = name; + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; + + r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q); + if (r < 0) + goto fail; + + if (!IN_SET(q.family, AF_INET, AF_INET6)) + continue; + + if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) { + r = -EINVAL; + goto fail; + } + + c++; + } + + canonical = p.name ?: name; alen = FAMILY_ADDRESS_SIZE(af); l = strlen(canonical); - ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); + ms = ALIGN(l+1) + c*ALIGN(alen) + (c+2) * sizeof(char*); if (buflen < ms) { UNPROTECT_ERRNO; @@ -375,45 +410,24 @@ enum nss_status _nss_resolve_gethostbyname3_r( /* Third, append addresses */ r_addr = buffer + idx; - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); - if (r < 0) - goto fail; + JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { + AddressParameters q = {}; - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { - int ifindex, family; - const void *a; - size_t sz; - - r = sd_bus_message_read(reply, "ii", &ifindex, &family); + r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q); if (r < 0) goto fail; - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (family != af) + if (q.family != af) continue; - if (sz != alen) { + if (q.address_size != alen) { r = -EINVAL; goto fail; } - memcpy(r_addr + i*ALIGN(alen), a, alen); + memcpy(r_addr + i*ALIGN(alen), &q.address, alen); i++; } - if (r < 0) - goto fail; assert(i == c); idx += c * ALIGN(alen); @@ -458,6 +472,40 @@ not_found: return NSS_STATUS_NOTFOUND; } +typedef struct ResolveAddressReply { + JsonVariant *names; + uint64_t flags; +} ResolveAddressReply; + +static void resolve_address_reply_destroy(ResolveAddressReply *p) { + assert(p); + + json_variant_unref(p->names); +} + +static const JsonDispatch resolve_address_reply_dispatch_table[] = { + { "names", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(ResolveAddressReply, names), JSON_MANDATORY }, + { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(ResolveAddressReply, flags), 0 }, + {} +}; + +typedef struct NameParameters { + int ifindex; + char *name; +} NameParameters; + +static void name_parameters_destroy(NameParameters *p) { + assert(p); + + free(p->name); +} + +static const JsonDispatch name_parameters_dispatch_table[] = { + { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0 }, + { "name", JSON_VARIANT_UNSIGNED, json_dispatch_string, offsetof(NameParameters, name), JSON_MANDATORY }, + {} +}; + enum nss_status _nss_resolve_gethostbyaddr2_r( const void* addr, socklen_t len, int af, @@ -466,14 +514,15 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( int *errnop, int *h_errnop, int32_t *ttlp) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {}; + _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(varlink_unrefp) Varlink *link = NULL; + JsonVariant *entry, *rparams; + const char *n, *error_id; unsigned c = 0, i = 0; size_t ms = 0, idx; - const char *n; - int r, ifindex; + int r; PROTECT_ERRNO; BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); @@ -496,70 +545,42 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( goto fail; } - if (avoid_deadlock()) { - r = -EDEADLK; - goto fail; - } - - r = sd_bus_open_system(&bus); + r = connect_to_resolved(&link); if (r < 0) goto fail; - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress"); + r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(addr, len)), + JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)))); if (r < 0) goto fail; - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "ii", 0, af); - if (r < 0) - goto fail; - - r = sd_bus_message_append_array(req, 'y', addr, len); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "t", (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL); if (r < 0) { - if (!bus_error_shall_fallback(&error)) + if (!error_shall_fallback(error_id)) goto not_found; goto fail; } - r = sd_bus_message_enter_container(reply, 'a', "(is)"); + r = json_dispatch(rparams, resolve_address_reply_dispatch_table, NULL, 0, &p); if (r < 0) goto fail; - - while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - c++; - ms += ALIGN(strlen(n) + 1); - } - if (r < 0) - goto fail; - - r = sd_bus_message_rewind(reply, false); - if (r < 0) - goto fail; - - if (c <= 0) + if (json_variant_is_blank_object(p.names)) goto not_found; - ms += ALIGN(len) + /* the address */ - 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ - c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ + JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { + _cleanup_(name_parameters_destroy) NameParameters q = {}; + + r = json_dispatch(entry, name_parameters_dispatch_table, NULL, 0, &q); + if (r < 0) + goto fail; + + ms += ALIGN(strlen(q.name) + 1); + } + + ms += ALIGN(len) + /* the address */ + 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ + json_variant_elements(p.names) * sizeof(char*); /* pointers to aliases, plus trailing NULL */ if (buflen < ms) { UNPROTECT_ERRNO; @@ -586,22 +607,25 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( /* Fourth, place aliases */ i = 0; r_name = buffer + idx; - while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - char *p; + JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { + _cleanup_(name_parameters_destroy) NameParameters q = {}; size_t l; + char *z; - l = strlen(n); - p = buffer + idx; - memcpy(p, n, l+1); + r = json_dispatch(entry, name_parameters_dispatch_table, NULL, 0, &q); + if (r < 0) + goto fail; + + l = strlen(q.name); + z = buffer + idx; + memcpy(z, n, l+1); if (i > 0) - ((char**) r_aliases)[i-1] = p; + ((char**) r_aliases)[i-1] = z; i++; idx += ALIGN(l+1); } - if (r < 0) - goto fail; ((char**) r_aliases)[c-1] = NULL; assert(idx == ms);