Merge pull request #16757 from poettering/nss-resolve-varlink

resolved: use varlink for communication between nss-resolve and resolved
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-08-26 22:07:34 +02:00 committed by GitHub
commit bb2aee7d11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1035 additions and 390 deletions

View file

@ -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 {

View file

@ -7,10 +7,6 @@
#include <sys/types.h>
#include <unistd.h>
#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);

View file

@ -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('''

View file

@ -24,45 +24,81 @@
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) {
assert(q);
assert(q->bus_request);
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 +119,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:
@ -139,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;
@ -152,7 +189,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 +197,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;
@ -168,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)
@ -190,7 +226,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;
}
@ -198,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,
@ -218,15 +254,29 @@ 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);
}
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");
@ -324,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;
@ -350,10 +400,9 @@ 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;
q->suppress_unroutable_family = family == AF_UNSPEC;
r = dns_query_bus_track(q, message);
if (r < 0)
@ -386,7 +435,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)
@ -394,7 +443,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;
@ -428,7 +477,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;
}
@ -446,7 +495,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);
@ -478,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;
@ -490,7 +539,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;
@ -555,7 +604,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)
@ -563,7 +612,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;
@ -588,7 +637,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;
}
@ -605,7 +654,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);
@ -643,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;
@ -667,7 +716,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);
@ -891,7 +940,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;
}
@ -904,7 +953,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;
@ -913,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)
@ -933,7 +983,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;
}
@ -979,7 +1029,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);
@ -1071,7 +1121,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)
@ -1115,12 +1165,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;
}
@ -1131,7 +1181,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);
@ -1177,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;
@ -1193,7 +1243,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;
@ -1690,7 +1740,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);
@ -1863,7 +1913,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;

View file

@ -338,9 +338,14 @@ 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);
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);
@ -473,14 +478,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 +991,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);

View file

@ -4,6 +4,7 @@
#include "sd-bus.h"
#include "set.h"
#include "varlink.h"
typedef struct DnsQueryCandidate DnsQueryCandidate;
typedef struct DnsQuery DnsQuery;
@ -48,10 +49,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;
@ -72,8 +69,9 @@ struct DnsQuery {
int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
bool previous_redirect_unauthenticated;
/* Bus client information */
sd_bus_message *request;
/* 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;
@ -116,7 +114,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);

View file

@ -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);

View file

@ -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 */

View file

@ -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);
}

View file

@ -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);

View file

@ -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)) {

View file

@ -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);
@ -302,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

View file

@ -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;

View file

@ -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);