diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index c6727ac760..193ee714d0 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -248,6 +248,61 @@ int json_dispatch_in6_addr(const char *name, sd_json_variant *variant, sd_json_d return 0; } +int json_dispatch_in_addr_data(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + struct in_addr_data *ret = ASSERT_PTR(userdata), data = { .family = AF_UNSPEC, .address = IN_ADDR_NULL }; + int r; + + assert(variant); + + if (sd_json_variant_is_null(variant)) { + *ret = data; + return 0; + } + + /* We support both a more human readable string based encoding and an array based encoding */ + if (sd_json_variant_is_string(variant)) { + r = in_addr_from_string_auto(sd_json_variant_string(variant), &data.family, &data.address); + if (r < 0) + return json_log(variant, flags, r, + "JSON field '%s' is not a valid IPv4 or IPv6 address string: %s", strna(name), sd_json_variant_string(variant)); + *ret = data; + return 0; + } + + if (!sd_json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); + + switch (sd_json_variant_elements(variant)) { + case sizeof(struct in_addr): + data.family = AF_INET; + break; + case sizeof(struct in6_addr): + data.family = AF_INET6; + break; + default: + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name)); + } + + size_t k = 0; + sd_json_variant *i; + JSON_VARIANT_ARRAY_FOREACH(i, variant) { + if (!sd_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)); + + int64_t b = sd_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%s255.", + k, strna(name), glyph(GLYPH_ELLIPSIS)); + + data.address.bytes[k++] = (uint8_t) b; + } + assert(k == FAMILY_ADDRESS_SIZE_SAFE(data.family)); + + *ret = data; + return 0; +} + int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { const char **p = ASSERT_PTR(userdata), *path; @@ -918,3 +973,27 @@ int json_variant_compare(sd_json_variant *a, sd_json_variant *b) { return CMP(sd_json_variant_type(a), sd_json_variant_type(b)); } + +int json_dispatch_address_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + int *family = ASSERT_PTR(userdata), r; + + assert(variant); + + if (sd_json_variant_is_negative(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), + "JSON field '%s' for an address family cannot be negative.", strna(name)); + + int k = AF_UNSPEC; + if (!sd_json_variant_is_null(variant)) { + r = sd_json_dispatch_int(name, variant, flags, &k); + if (r < 0) + return r; + } + + if (!IN_SET(k, AF_INET, AF_INET6) && !(FLAGS_SET(flags, SD_JSON_RELAX) && k == AF_UNSPEC)) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "JSON field '%s' out of range for an address family.", strna(name)); + + *family = k; + return 0; +} diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h index ecb4e2d633..2032566fdd 100644 --- a/src/libsystemd/sd-json/json-util.h +++ b/src/libsystemd/sd-json/json-util.h @@ -119,6 +119,7 @@ int json_dispatch_const_user_group_name(const char *name, sd_json_variant *varia int json_dispatch_const_unit_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_in6_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); +int json_dispatch_in_addr_data(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_strv_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); @@ -133,6 +134,7 @@ int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_ int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_job_id(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); +int json_dispatch_address_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) { return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL); diff --git a/src/libsystemd/sd-json/test-json.c b/src/libsystemd/sd-json/test-json.c index bb94dfea4b..6992d095c6 100644 --- a/src/libsystemd/sd-json/test-json.c +++ b/src/libsystemd/sd-json/test-json.c @@ -13,6 +13,7 @@ #include "fd-util.h" #include "format-util.h" #include "fileio.h" +#include "in-addr-util.h" #include "io-util.h" #include "iovec-util.h" #include "json-internal.h" @@ -1998,4 +1999,197 @@ TEST(json_dispatch_in6_addr) { ASSERT_EQ(data.addr.s6_addr[i], 0); } +TEST(json_dispatch_in_addr_data) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; + + /* 192.168.1.1 = { 192, 168, 1, 1 } */ + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", JSON_BUILD_IN4_ADDR(&(const struct in_addr) { .s_addr = htobe32(0xC0A80101U) })), + SD_JSON_BUILD_PAIR("null_addr", SD_JSON_BUILD_NULL)))); + + struct { + struct in_addr_data addr; + struct in_addr_data null_addr; + } data = {}; + + ASSERT_OK(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, offsetof(typeof(data), addr) }, + { "null_addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, offsetof(typeof(data), null_addr) }, + {}, + }, + /* flags= */ 0, + &data)); + + ASSERT_EQ(be32toh(data.addr.address.in.s_addr), 0xC0A80101U); + ASSERT_EQ(data.null_addr.address.in.s_addr, 0U); + ASSERT_EQ(data.addr.family, AF_INET); + ASSERT_EQ(data.null_addr.family, AF_UNSPEC); + + struct in_addr_data dummy = {}; + + /* Too few bytes (3 instead of 4) */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_UNSIGNED(192), SD_JSON_BUILD_UNSIGNED(168), SD_JSON_BUILD_UNSIGNED(1)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* Too many bytes (5 instead of 4) */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_UNSIGNED(192), SD_JSON_BUILD_UNSIGNED(168), SD_JSON_BUILD_UNSIGNED(1), SD_JSON_BUILD_UNSIGNED(1), SD_JSON_BUILD_UNSIGNED(0)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* Not an array or string */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_BOOLEAN(true))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* A string */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", JSON_BUILD_CONST_STRING("192.168.1.1"))))); + zero(data); + ASSERT_OK(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &data)); + ASSERT_EQ(be32toh(data.addr.address.in.s_addr), 0xC0A80101U); + ASSERT_EQ(data.addr.family, AF_INET); + + /* Byte value out of range (> 255) */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_UNSIGNED(256), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(1)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* Negative element */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY(SD_JSON_BUILD_INTEGER(-1), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(1)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* ::1 */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", JSON_BUILD_IN6_ADDR(&(const struct in6_addr) { .s6_addr = { [15] = 1 } })), + SD_JSON_BUILD_PAIR("null_addr", SD_JSON_BUILD_NULL)))); + + zero(data); + ASSERT_OK(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, offsetof(typeof(data), addr) }, + { "null_addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, offsetof(typeof(data), null_addr) }, + {}, + }, + /* flags= */ 0, + &data)); + + ASSERT_EQ(data.addr.address.in6.s6_addr[15], 1); + for (size_t i = 0; i < 15; i++) + ASSERT_EQ(data.addr.address.in6.s6_addr[i], 0); + for (size_t i = 0; i < 16; i++) + ASSERT_EQ(data.null_addr.address.in6.s6_addr[i], 0); + ASSERT_EQ(data.addr.family, AF_INET6); + ASSERT_EQ(data.null_addr.family, AF_UNSPEC); + + /* Too few bytes (15 instead of 16) */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY( + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(1)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* Too many bytes (17 instead of 16) */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_ARRAY( + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), + SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(0), SD_JSON_BUILD_UNSIGNED(1), + SD_JSON_BUILD_UNSIGNED(0)))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* Not an array */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", SD_JSON_BUILD_BOOLEAN(true))))); + ASSERT_ERROR(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &dummy), EINVAL); + + /* A string */ + j = sd_json_variant_unref(j); + ASSERT_OK(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("addr", JSON_BUILD_CONST_STRING("::1"))))); + + zero(data); + ASSERT_OK(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "addr", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, 0 }, + {}, + }, + /* flags= */ 0, + &data)); + + ASSERT_EQ(data.addr.address.in6.s6_addr[15], 1); + for (size_t i = 0; i < 15; i++) + ASSERT_EQ(data.addr.address.in6.s6_addr[i], 0); + ASSERT_EQ(data.addr.family, AF_INET6); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/network/networkd-serialize.c b/src/network/networkd-serialize.c index 394f03ced6..224fcc93a0 100644 --- a/src/network/networkd-serialize.c +++ b/src/network/networkd-serialize.c @@ -102,28 +102,6 @@ int manager_set_serialization_fd(Manager *manager, int fd, const char *name) { static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_network_config_source, NetworkConfigSource, network_config_source_from_string); -static int json_dispatch_address_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - int r, *i = ASSERT_PTR(userdata); - int64_t i64; - - assert_return(variant, -EINVAL); - - if (FLAGS_SET(flags, SD_JSON_RELAX) && sd_json_variant_is_null(variant)) { - *i = AF_UNSPEC; - return 0; - } - - r = sd_json_dispatch_int64(name, variant, flags, &i64); - if (r < 0) - return r; - - if (!IN_SET(i64, AF_INET, AF_INET6) && !(FLAGS_SET(flags, SD_JSON_RELAX) && i64 == AF_UNSPEC)) - return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds for an address family.", strna(name)); - - *i = (int) i64; - return 0; -} - typedef struct AddressParam { int family; struct iovec address; diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 86eaf4e3b3..55e4216f53 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -14,6 +14,7 @@ #include "json-util.h" #include "netlink-util.h" #include "nss-util.h" +#include "resolve-varlink-util.h" #include "resolved-def.h" #include "signal-util.h" #include "string-util.h" @@ -64,107 +65,6 @@ static int connect_to_resolved(sd_varlink **ret) { return 0; } -static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) { - struct in6_addr in6; - - if (family != AF_INET6 || ifindex == 0) - return 0; - - /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */ - - assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6)); - memcpy(&in6, a, sizeof(struct in6_addr)); - - return in6_addr_is_link_local(&in6) ? ifindex : 0; -} - -static int json_dispatch_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - int *family = ASSERT_PTR(userdata); - int64_t t; - - assert(variant); - - if (!sd_json_variant_is_integer(variant)) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); - - t = sd_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 { - sd_json_variant *addresses; - char *name; - uint64_t flags; -} ResolveHostnameReply; - -static void resolve_hostname_reply_destroy(ResolveHostnameReply *p) { - assert(p); - - sd_json_variant_unref(p->addresses); - free(p->name); -} - -static const sd_json_dispatch_field resolve_hostname_reply_dispatch_table[] = { - { "addresses", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(ResolveHostnameReply, addresses), SD_JSON_MANDATORY }, - { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolveHostnameReply, name), 0 }, - { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_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, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { - AddressParameters *p = ASSERT_PTR(userdata); - union in_addr_union buf = {}; - sd_json_variant *i; - size_t n, k = 0; - - assert(variant); - - if (!sd_json_variant_is_array(variant)) - return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); - - n = sd_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) { - int64_t b; - - if (!sd_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 = sd_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%s255.", - k, strna(name), glyph(GLYPH_ELLIPSIS)); - - buf.bytes[k++] = (uint8_t) b; - } - - p->address = buf; - p->address_size = k; - - return 0; -} - -static const sd_json_dispatch_field address_parameters_dispatch_table[] = { - { "ifindex", SD_JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(AddressParameters, ifindex), 0 }, - { "family", SD_JSON_VARIANT_INTEGER, json_dispatch_family, offsetof(AddressParameters, family), SD_JSON_MANDATORY }, - { "address", SD_JSON_VARIANT_ARRAY, json_dispatch_address, 0, SD_JSON_MANDATORY }, - {} -}; - static uint64_t query_flag( const char *name, const int value, @@ -208,6 +108,19 @@ static int query_ifindex(void) { return ifindex; } +static uint32_t ifindex_to_scopeid(ResolvedAddress *addr) { + assert(addr); + + if (addr->family != AF_INET6 || addr->ifindex == 0) + return 0; + + /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */ + if (in6_addr_is_link_local(&addr->in_addr.address.in6)) + return addr->ifindex; + + return 0; +} + enum nss_status _nss_resolve_gethostbyname4_r( const char *name, struct gaih_addrtuple **pat, @@ -217,8 +130,8 @@ enum nss_status _nss_resolve_gethostbyname4_r( _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *cparams = NULL; - _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {}; - sd_json_variant *rparams, *entry; + _cleanup_(resolve_hostname_reply_done) ResolveHostnameReply p = {}; + sd_json_variant *rparams; int r; PROTECT_ERRNO; @@ -261,28 +174,17 @@ enum nss_status _nss_resolve_gethostbyname4_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, nss_json_dispatch_flags, &p); + r = dispatch_resolve_hostname_reply(NULL, rparams, nss_json_dispatch_flags, &p); if (r < 0) goto fail; - if (sd_json_variant_is_blank_array(p.addresses)) + if (p.n_addresses == 0) goto not_found; size_t n_addresses = 0; - JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { - AddressParameters q = {}; - - r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - if (!IN_SET(q.family, AF_INET, AF_INET6)) + FOREACH_ARRAY(entry, p.addresses, p.n_addresses) { + if (!IN_SET(entry->family, AF_INET, AF_INET6)) continue; - if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) { - r = -EINVAL; - goto fail; - } - n_addresses++; } @@ -309,22 +211,16 @@ enum nss_status _nss_resolve_gethostbyname4_r( struct gaih_addrtuple *r_tuple = NULL, *r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { - AddressParameters q = {}; - - r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - if (!IN_SET(q.family, AF_INET, AF_INET6)) + FOREACH_ARRAY(entry, p.addresses, p.n_addresses) { + if (!IN_SET(entry->family, AF_INET, AF_INET6)) continue; r_tuple = (struct gaih_addrtuple*) (buffer + idx); r_tuple->next = (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); r_tuple->name = r_name; - 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); + r_tuple->family = entry->family; + r_tuple->scopeid = ifindex_to_scopeid(entry); + memcpy(r_tuple->addr, entry->in_addr.address.bytes, FAMILY_ADDRESS_SIZE_SAFE(entry->family)); idx += ALIGN(sizeof(struct gaih_addrtuple)); } @@ -381,8 +277,8 @@ enum nss_status _nss_resolve_gethostbyname3_r( _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *cparams = NULL; - _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {}; - sd_json_variant *rparams, *entry; + _cleanup_(resolve_hostname_reply_done) ResolveHostnameReply p = {}; + sd_json_variant *rparams; int r; PROTECT_ERRNO; @@ -429,28 +325,17 @@ enum nss_status _nss_resolve_gethostbyname3_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, nss_json_dispatch_flags, &p); + r = dispatch_resolve_hostname_reply(NULL, rparams, nss_json_dispatch_flags, &p); if (r < 0) goto fail; - if (sd_json_variant_is_blank_array(p.addresses)) + if (p.n_addresses == 0) goto no_data; size_t n_addresses = 0; - JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { - AddressParameters q = {}; - - r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - if (q.family != af) + FOREACH_ARRAY(entry, p.addresses, p.n_addresses) { + if (entry->family != af) continue; - if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) { - r = -EINVAL; - goto fail; - } - n_addresses++; } @@ -485,22 +370,16 @@ enum nss_status _nss_resolve_gethostbyname3_r( char *r_addr = buffer + idx; size_t i = 0; - JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { - AddressParameters q = {}; - - r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - if (q.family != af) + FOREACH_ARRAY(entry, p.addresses, p.n_addresses) { + if (entry->family != af) continue; - if (q.address_size != alen) { + if (FAMILY_ADDRESS_SIZE_SAFE(entry->family) != alen) { r = -EINVAL; goto fail; } - memcpy(r_addr + i*ALIGN(alen), &q.address, alen); + memcpy(r_addr + i*ALIGN(alen), entry->in_addr.address.bytes, alen); i++; } @@ -557,40 +436,6 @@ try_again: return NSS_STATUS_TRYAGAIN; } -typedef struct ResolveAddressReply { - sd_json_variant *names; - uint64_t flags; -} ResolveAddressReply; - -static void resolve_address_reply_destroy(ResolveAddressReply *p) { - assert(p); - - sd_json_variant_unref(p->names); -} - -static const sd_json_dispatch_field resolve_address_reply_dispatch_table[] = { - { "names", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(ResolveAddressReply, names), SD_JSON_MANDATORY }, - { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_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 sd_json_dispatch_field name_parameters_dispatch_table[] = { - { "ifindex", SD_JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0 }, - { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(NameParameters, name), SD_JSON_MANDATORY }, - {} -}; - enum nss_status _nss_resolve_gethostbyaddr2_r( const void* addr, socklen_t len, int af, @@ -601,8 +446,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( _cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *cparams = NULL; - _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {}; - sd_json_variant *rparams, *entry; + _cleanup_(resolve_address_reply_done) ResolveAddressReply p = {}; + sd_json_variant *rparams; int r; PROTECT_ERRNO; @@ -651,25 +496,18 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_address_reply_dispatch_table, nss_json_dispatch_flags, &p); + r = dispatch_resolve_address_reply(NULL, rparams, nss_json_dispatch_flags, &p); if (r < 0) goto fail; - if (sd_json_variant_is_blank_array(p.names)) + if (p.n_names == 0) goto not_found; size_t ms = 0, idx; - JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { - _cleanup_(name_parameters_destroy) NameParameters q = {}; + FOREACH_ARRAY(entry, p.names, p.n_names) + ms += ALIGN(strlen(entry->name) + 1); - r = sd_json_dispatch(entry, name_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - ms += ALIGN(strlen(q.name) + 1); - } - - size_t n_names = sd_json_variant_elements(p.names); + size_t n_names = p.n_names; ms += ALIGN(len) + /* the address */ 2 * sizeof(char*) + /* pointer to the address, plus trailing NULL */ n_names * sizeof(char*); /* pointers to aliases, plus trailing NULL */ @@ -700,16 +538,10 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( char *r_name = buffer + idx; size_t i = 0; - JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { - _cleanup_(name_parameters_destroy) NameParameters q = {}; - - r = sd_json_dispatch(entry, name_parameters_dispatch_table, nss_json_dispatch_flags, &q); - if (r < 0) - goto fail; - - size_t l = strlen(q.name); + FOREACH_ARRAY(entry, p.names, p.n_names) { + size_t l = strlen(entry->name); char *z = buffer + idx; - memcpy(z, q.name, l + 1); + memcpy(z, entry->name, l + 1); if (i > 0) ((char**) r_aliases)[i - 1] = z; diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 67be81195a..af7ed43672 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -18,7 +18,6 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-locator.h" -#include "bus-message-util.h" #include "bus-util.h" #include "crypto-util.h" #include "dns-configuration.h" @@ -27,7 +26,6 @@ #include "dns-rr.h" #include "errno-list.h" #include "errno-util.h" -#include "escape.h" #include "format-ifname.h" #include "format-table.h" #include "glyph-util.h" @@ -48,6 +46,7 @@ #include "resolvectl.h" #include "resolved-def.h" #include "resolved-util.h" +#include "resolve-varlink-util.h" #include "set.h" #include "socket-netlink.h" #include "string-table.h" @@ -278,20 +277,101 @@ static void print_ifindex_comment(int printed_so_far, int ifindex) { ansi_grey(), ifname, ansi_normal()); } -static int resolve_host_error(const char *name, int r, const sd_bus_error *error) { - if (sd_bus_error_has_name(error, BUS_ERROR_DNS_NXDOMAIN)) - return log_error_errno(r, "%s: %s", name, bus_error_message(error, r)); +static int varlink_log_resolve_error(const char *name, const char *error_id, sd_json_variant *reply, bool warn_missing) { + int r; - return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(error, r)); + assert(name); + assert(!isempty(error_id)); + + int ret = sd_varlink_error_to_errno(error_id, reply); + static const struct { + const char *error_id; + const char *msg; + } error_message_table[] = { + { "io.systemd.Resolve.NoNameServers", "No appropriate name servers or networks for name found" }, + { "io.systemd.Resolve.QueryTimedOut", "Query timed out" }, + { "io.systemd.Resolve.MaxAttemptsReached", "All attempts to contact name servers or networks failed" }, + { "io.systemd.Resolve.InvalidReply", "Received invalid reply" }, + { "io.systemd.Resolve.QueryAborted", "Query aborted" }, + { "io.systemd.Resolve.QueryRefused", "DNS query type refused" }, + { "io.systemd.Resolve.NoTrustAnchor", "No suitable trust anchor known" }, + { "io.systemd.Resolve.ResourceRecordTypeUnsupported", "Server does not support requested resource record type" }, + { "io.systemd.Resolve.NetworkDown", "Network is down" }, + { "io.systemd.Resolve.NoSource", "All suitable resolution sources turned off" }, + { "io.systemd.Resolve.StubLoop", "Configured DNS server loops back to us" }, + { "io.systemd.Resolve.ZoneTransfersNotPermitted", "Zone transfers not permitted via this programming interface" }, + }; + FOREACH_ELEMENT(em, error_message_table) + if (streq(em->error_id, error_id)) + return log_error_errno(ret, "%s: resolve call failed: %s", name, em->msg); + + if (streq(error_id, "io.systemd.Resolve.NoSuchResourceRecord")) + return log_error_errno(ret, "%s: resolve call failed: '%s' does not have any RR of the requested type", name, name); + + if (streq(error_id, "io.systemd.Resolve.CNAMELoop")) + return log_error_errno(ret, "%s: resolve call failed: CNAME loop detected, or CNAME resolving disabled on '%s'", name, name); + + if (streq(error_id, "io.systemd.Resolve.ServiceNotProvided")) + return log_error_errno(ret, "%s: resolve call failed: '%s' does not provide the requested service", name, name); + + if (streq(error_id, "io.systemd.Resolve.InconsistentServiceRecords")) + return log_error_errno(ret, "%s: resolve call failed: '%s' does not provide a consistent set of service resource records", name, name); + + _cleanup_(resolve_error_done) ResolveError error = { + .rcode = _DNS_RCODE_INVALID, + .ede_rcode = _DNS_EDE_RCODE_INVALID, + }; + r = dispatch_resolve_error(/* name = */ NULL, reply, SD_JSON_LOG, &error); + if (r < 0) + log_debug_errno(r, "Failed to dispatch error JSON, ignoring: %m"); + + _cleanup_free_ char *msg_extended = NULL; + if (error.ede_rcode >= 0) { + msg_extended = strjoin(" (", + FORMAT_DNS_EDE_RCODE(error.ede_rcode), + !isempty(error.ede_msg) ? ": " : "", + strempty(error.ede_msg), + ")"); + if (!msg_extended) + return log_oom(); + } + + if (streq(error_id, "io.systemd.Resolve.DNSSECValidationFailed")) + return log_error_errno(ret, "%s: resolve call failed: DNSSEC validation failed: %s%s", name, error.result, strempty(msg_extended)); + + if (error.rcode != _DNS_RCODE_INVALID) { + if (error.rcode == DNS_RCODE_NXDOMAIN) { + if (!warn_missing) + return -ENXIO; + + return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "%s: resolve call failed: Name '%s' not found%s%s", + name, error.query_string ?: name, error.ede_rcode >= 0 ? ":" : "", strempty(msg_extended)); + } + + return log_error_errno(ret, "%s: resolve call failed: Could not resolve '%s', server or network returned error: %s%s", + name, error.query_string ?: name, FORMAT_DNS_RCODE(error.rcode), strempty(msg_extended)); + } + + return log_error_errno(ret, "%s: resolve call failed: %s", name, error_id); } -static int resolve_host(sd_bus *bus, const char *name) { - _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; - const char *canonical = NULL; - unsigned c = 0; - uint64_t flags; - usec_t ts; +static int varlink_connect_with_query_timeout(sd_varlink **vl) { + int r; + + assert(vl); + + r = sd_varlink_connect_address(vl, "/run/systemd/resolve/io.systemd.Resolve"); + if (r < 0) + return log_error_errno(r, "Failed to connect to service /run/systemd/resolve/io.systemd.Resolve: %m"); + + r = sd_varlink_set_relative_timeout(*vl, SD_RESOLVED_QUERY_TIMEOUT_USEC); + if (r < 0) + return log_error_errno(r, "Failed to set query timeout: %m"); + + return 0; +} + +static int resolve_host(const char *name) { int r; assert(name); @@ -301,99 +381,76 @@ static int resolve_host(sd_bus *bus, const char *name) { log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname); - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname"); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + r = varlink_connect_with_query_timeout(&vl); if (r < 0) - return bus_log_create_error(r); + return r; - r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags); + usec_t ts = now(CLOCK_MONOTONIC); + + const char *error_id = NULL; + sd_json_variant *v = NULL; + r = sd_varlink_callbo( + vl, + "io.systemd.Resolve.ResolveHostname", + &v, + &error_id, + SD_JSON_BUILD_PAIR_STRING("name", name), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_family != AF_UNSPEC, "family", arg_family), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); if (r < 0) - return bus_log_create_error(r); - - ts = now(CLOCK_MONOTONIC); - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); - if (r < 0) - return resolve_host_error(name, r, &error); + return log_error_errno(r, "Failed to issue varlink call: %m"); ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); + if (!isempty(error_id)) + return varlink_log_resolve_error(name, error_id, v, /* warn_missing = */ true); + + _cleanup_(resolve_hostname_reply_done) ResolveHostnameReply reply = {}; + r = dispatch_resolve_hostname_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); if (r < 0) - return bus_log_parse_error(r); + return r; - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + bool first = true; + FOREACH_ARRAY(address, reply.addresses, reply.n_addresses) { _cleanup_free_ char *pretty = NULL; - int ifindex, family, k; - union in_addr_union a; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - return bus_log_parse_error(r); - - sd_bus_error_free(&error); - r = bus_message_read_in_addr_auto(reply, &error, &family, &a); - if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) - return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r)); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) { - log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r)); - continue; - } - - r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty); + r = in_addr_ifindex_to_string(address->family, &address->in_addr.address, address->ifindex, &pretty); if (r < 0) return log_error_errno(r, "Failed to print address for %s: %m", name); - k = printf("%*s%s %s%s%s", - (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", - ansi_highlight(), pretty, ansi_normal()); + int k = printf("%*s%s %s%s%s", + (int) strlen(name), + first ? name : "", + first ? ":" : " ", + ansi_highlight(), + pretty, + ansi_normal()); - print_ifindex_comment(k, ifindex); + print_ifindex_comment(k, address->ifindex); fputc('\n', stdout); - c++; + first = false; } - if (r < 0) - return bus_log_parse_error(r); - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(reply, "st", &canonical, &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (!streq(name, canonical)) + if (!streq(name, reply.name)) printf("%*s%s (%s)\n", - (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", - canonical); + (int) strlen(name), + reply.n_addresses == 0 ? name : "", + reply.n_addresses == 0 ? ":" : " ", + reply.name); - if (c == 0) - return log_error_errno(SYNTHETIC_ERRNO(ESRCH), - "%s: no addresses found", name); + if (reply.n_addresses == 0) + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "%s: no addresses found", name); - print_source(flags, ts); + print_source(reply.flags, ts); return 0; } -static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) { - _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_free_ char *pretty = NULL; - uint64_t flags; - unsigned c = 0; - usec_t ts; +static int resolve_address(int family, const union in_addr_union *address, int ifindex) { int r; - assert(bus); assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); @@ -403,93 +460,72 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a if (ifindex <= 0) ifindex = arg_ifindex; + _cleanup_free_ char *pretty = NULL; r = in_addr_ifindex_to_string(family, address, ifindex, &pretty); if (r < 0) return log_oom(); log_debug("Resolving %s.", pretty); - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress"); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + r = varlink_connect_with_query_timeout(&vl); if (r < 0) - return bus_log_create_error(r); + return r; - r = sd_bus_message_append(req, "ii", ifindex, family); + usec_t ts = now(CLOCK_MONOTONIC); + + const char *error_id = NULL; + sd_json_variant *v = NULL; + r = sd_varlink_callbo( + vl, + "io.systemd.Resolve.ResolveAddress", + &v, + &error_id, + SD_JSON_BUILD_PAIR_BYTE_ARRAY("address", &address->bytes, FAMILY_ADDRESS_SIZE_SAFE(family)), + SD_JSON_BUILD_PAIR_UNSIGNED("family", family), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(ifindex > 0, "ifindex", ifindex), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "t", arg_flags); - if (r < 0) - return bus_log_create_error(r); - - ts = now(CLOCK_MONOTONIC); - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); - if (r < 0) - return log_error_errno(r, "%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); + return log_error_errno(r, "Failed to issue varlink call: %m"); ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_enter_container(reply, 'a', "(is)"); + if (!isempty(error_id)) + return varlink_log_resolve_error(pretty, error_id, v, /* warn_missing = */ true); + + _cleanup_(resolve_address_reply_done) ResolveAddressReply reply = {}; + r = dispatch_resolve_address_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); if (r < 0) - return bus_log_create_error(r); + return r; - while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) { - const char *n; - int k; + bool first = true; + FOREACH_ARRAY(name, reply.names, reply.n_names) { + int k = printf("%*s%s %s%s%s", + (int) strlen(pretty), + first ? pretty : "", + first ? ":" : " ", + ansi_highlight(), + name->name, + ansi_normal()); - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "is", &ifindex, &n); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - k = printf("%*s%s %s%s%s", - (int) strlen(pretty), c == 0 ? pretty : "", - c == 0 ? ":" : " ", - ansi_highlight(), n, ansi_normal()); - - print_ifindex_comment(k, ifindex); + print_ifindex_comment(k, name->ifindex); fputc('\n', stdout); - c++; + first = false; } - if (r < 0) - return bus_log_parse_error(r); - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); + if (reply.n_names == 0) + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "%s: no names found", pretty); - r = sd_bus_message_read(reply, "t", &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (c == 0) - return log_error_errno(SYNTHETIC_ERRNO(ESRCH), - "%s: no names found", pretty); - - print_source(flags, ts); + print_source(reply.flags, ts); return 0; } -static int output_rr_packet(const void *d, size_t l, int ifindex) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; +static int output_rr_packet(DnsResourceRecord *rr, int ifindex) { int r; - assert(d || l == 0); - - r = dns_resource_record_new_from_raw(&rr, d, l); - if (r < 0) - return log_error_errno(r, "Failed to parse RR: %m"); + assert(rr); if (sd_json_format_enabled(arg_json_format_flags)) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; @@ -572,14 +608,7 @@ static bool single_label_nonsynthetic(const char *name) { return !streq(name, first_label); } -static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type, bool warn_missing) { - _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_free_ char *idnafied = NULL; - bool needs_authentication = false; - unsigned n = 0; - uint64_t flags; - usec_t ts; +static int resolve_record(const char *name, uint16_t class, uint16_t type, bool warn_missing) { int r; assert(name); @@ -590,6 +619,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ log_notice("(Note that search domains are not appended when --type= is specified. " "Please specify fully qualified domain names, or remove --type= switch from invocation in order to request regular hostname resolution.)"); + _cleanup_free_ char *idnafied = NULL; r = idna_candidate(name, &idnafied); if (r < 0) return r; @@ -598,85 +628,76 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ "Please specify translated domain names — i.e. '%s' — when resolving raw records, or remove --type= switch from invocation in order to request regular hostname resolution.", idnafied); - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveRecord"); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + r = varlink_connect_with_query_timeout(&vl); if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags); - if (r < 0) - return bus_log_create_error(r); - - ts = now(CLOCK_MONOTONIC); - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - if (warn_missing || r != -ENXIO) - log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); return r; - } + + usec_t ts = now(CLOCK_MONOTONIC); + + const char *error_id = NULL; + sd_json_variant *v = NULL; + r = sd_varlink_callbo( + vl, + "io.systemd.Resolve.ResolveRecord", + &v, + &error_id, + SD_JSON_BUILD_PAIR_STRING("name", name), + SD_JSON_BUILD_PAIR_UNSIGNED("type", type), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("class", class), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); + if (r < 0) + return log_error_errno(r, "Failed to issue varlink call: %m"); ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); + if (!isempty(error_id)) { + if (streq(error_id, "io.systemd.Resolve.ResourceRecordTypeInvalidForQuery")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified resource record type %" PRIu16 " may not be used in a query", type); + + if (streq(error_id, "io.systemd.Resolve.ResourceRecordTypeObsolete")) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Specified DNS resource record type %" PRIu16 " is obsolete", type); + + return varlink_log_resolve_error(name, error_id, v, warn_missing); + } + + _cleanup_(resolve_record_reply_done) ResolveRecordReply reply = {}; + r = dispatch_resolve_record_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); if (r < 0) - return bus_log_parse_error(r); + return r; - while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { - uint16_t c, t; - int ifindex; - const void *d; - size_t l; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); + bool needs_authentication = false; + FOREACH_ARRAY(record, reply.records, reply.n_records) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + r = dns_resource_record_new_from_raw(&rr, record->raw.iov_base, record->raw.iov_len); if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read_array(reply, 'y', &d, &l); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); + return log_error_errno(r, "Failed to parse RR: %m"); if (arg_raw == RAW_PACKET) { - uint64_t u64 = htole64(l); + uint64_t u64 = htole64(record->raw.iov_len); fwrite(&u64, sizeof(u64), 1, stdout); - fwrite(d, 1, l, stdout); + fwrite(record->raw.iov_base, 1, record->raw.iov_len, stdout); } else { - r = output_rr_packet(d, l, ifindex); + r = output_rr_packet(rr, record->ifindex); if (r < 0) return r; } - if (dns_type_needs_authentication(t)) + if (dns_type_needs_authentication(rr->key->type)) needs_authentication = true; - - n++; } - if (r < 0) - return bus_log_parse_error(r); - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(reply, "t", &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (n == 0) { + if (reply.n_records == 0) { if (warn_missing) log_error("%s: no records found", name); return -ESRCH; } - print_source(flags, ts); + print_source(reply.flags, ts); - if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { + if ((reply.flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { fflush(stdout); fprintf(stderr, "\n%s" @@ -691,12 +712,11 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ return 0; } -static int resolve_rfc4501(sd_bus *bus, const char *name) { +static int resolve_rfc4501(const char *name) { uint16_t type = 0, class = 0; const char *p, *q, *n; int r; - assert(bus); assert(name); assert(startswith(name, "dns:")); @@ -794,7 +814,7 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) { if (type == 0) type = arg_type ?: DNS_TYPE_A; - return resolve_record(bus, n, class, type, true); + return resolve_record(n, class, type, true); invalid: return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -804,48 +824,34 @@ invalid: VERB(verb_query, "query", "HOSTNAME|ADDRESS…", 2, VERB_ANY, 0, "Resolve domain names, IPv4 and IPv6 addresses"); static int verb_query(int argc, char *argv[], uintptr_t _data, void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int ret = 0, r; - r = acquire_bus(&bus); - if (r < 0) - return r; - if (arg_type != 0) STRV_FOREACH(p, strv_skip(argv, 1)) - RET_GATHER(ret, resolve_record(bus, *p, arg_class, arg_type, true)); + RET_GATHER(ret, resolve_record(*p, arg_class, arg_type, true)); else STRV_FOREACH(p, strv_skip(argv, 1)) { if (startswith(*p, "dns:")) - RET_GATHER(ret, resolve_rfc4501(bus, *p)); + RET_GATHER(ret, resolve_rfc4501(*p)); else { int family, ifindex; union in_addr_union a; r = in_addr_ifindex_from_string_auto(*p, &family, &a, &ifindex); if (r >= 0) - RET_GATHER(ret, resolve_address(bus, family, &a, ifindex)); + RET_GATHER(ret, resolve_address(family, &a, ifindex)); else - RET_GATHER(ret, resolve_host(bus, *p)); + RET_GATHER(ret, resolve_host(*p)); } } return ret; } -static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) { - const char *canonical_name, *canonical_type, *canonical_domain; - _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; - size_t indent, sz; - uint64_t flags; - const char *p; - unsigned c; - usec_t ts; +static int resolve_service(const char *name, const char *type, const char *domain) { int r; - assert(bus); assert(domain); name = empty_to_null(name); @@ -858,141 +864,83 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons else log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname); - r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveService"); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + r = varlink_connect_with_query_timeout(&vl); if (r < 0) - return bus_log_create_error(r); + return r; - r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags); + usec_t ts = now(CLOCK_MONOTONIC); + + const char *error_id = NULL; + sd_json_variant *v = NULL; + r = sd_varlink_callbo( + vl, + "io.systemd.Resolve.ResolveService", + &v, + &error_id, + SD_JSON_BUILD_PAIR_STRING("domain", domain), + JSON_BUILD_PAIR_STRING_NON_EMPTY("name", name), + JSON_BUILD_PAIR_STRING_NON_EMPTY("type", type), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_ifindex > 0, "ifindex", arg_ifindex), + JSON_BUILD_PAIR_CONDITION_UNSIGNED(arg_family != AF_UNSPEC, "family", arg_family), + JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("flags", arg_flags)); if (r < 0) - return bus_log_create_error(r); - - ts = now(CLOCK_MONOTONIC); - - r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); - if (r < 0) - return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to issue varlink call: %m"); ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)"); + if (!isempty(error_id)) + return varlink_log_resolve_error(domain, error_id, v, /* warn_missing = */ true); + + _cleanup_(resolve_service_reply_done) ResolveServiceReply reply = {}; + r = dispatch_resolve_service_reply(/* name = */ NULL, v, SD_JSON_LOG, &reply); if (r < 0) - return bus_log_parse_error(r); + return r; - indent = - (name ? strlen(name) + 1 : 0) + - (type ? strlen(type) + 1 : 0) + - strlen(domain) + 2; - - c = 0; - while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) { - uint16_t priority, weight, port; - const char *hostname, *canonical; - - r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname); - if (r < 0) - return bus_log_parse_error(r); + size_t indent = (name ? strlen(name) + 1 : 0) + + (type ? strlen(type) + 1 : 0) + + strlen(domain) + 2; + bool first = true; + FOREACH_ARRAY(service, reply.services, reply.n_services) { if (name) - printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " "); + printf("%*s%s", (int) strlen(name), first ? name : "", first ? "/" : " "); if (type) - printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " "); + printf("%*s%s", (int) strlen(type), first ? type : "", first ? "/" : " "); printf("%*s%s %s:%u [priority=%u, weight=%u]\n", - (int) strlen(domain), c == 0 ? domain : "", - c == 0 ? ":" : " ", - hostname, port, - priority, weight); + (int) strlen(domain), + first ? domain : "", + first ? ":" : " ", + service->hostname, + service->port, + service->priority, + service->weight); - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + FOREACH_ARRAY(address, service->addresses, service->n_addresses) { _cleanup_free_ char *pretty = NULL; - int ifindex, family, k; - union in_addr_union a; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - return bus_log_parse_error(r); - - sd_bus_error_free(&error); - r = bus_message_read_in_addr_auto(reply, &error, &family, &a); - if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) - return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r)); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) { - log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r)); - continue; - } - - r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty); + r = in_addr_ifindex_to_string(address->family, &address->in_addr.address, address->ifindex, &pretty); if (r < 0) return log_error_errno(r, "Failed to print address for %s: %m", name); - k = printf("%*s%s", (int) indent, "", pretty); - print_ifindex_comment(k, ifindex); + int k = printf("%*s%s", (int) indent, "", pretty); + + print_ifindex_comment(k, address->ifindex); fputc('\n', stdout); } - if (r < 0) - return bus_log_parse_error(r); - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); + if (service->canonical_name && !streq_ptr(service->hostname, service->canonical_name)) + printf("%*s(%s)\n", (int) indent, "", service->canonical_name); - r = sd_bus_message_read(reply, "s", &canonical); - if (r < 0) - return bus_log_parse_error(r); - - if (!streq(hostname, canonical)) - printf("%*s(%s)\n", (int) indent, "", canonical); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - c++; + first = false; } - if (r < 0) - return bus_log_parse_error(r); - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); + STRV_FOREACH(p, reply.txt) + printf("%*s%s\n", (int) indent, "", *p); - r = sd_bus_message_enter_container(reply, 'a', "ay"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) { - _cleanup_free_ char *escaped = NULL; - - escaped = cescape_length(p, sz); - if (!escaped) - return log_oom(); - - printf("%*s%s\n", (int) indent, "", escaped); - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags); - if (r < 0) - return bus_log_parse_error(r); - - canonical_name = empty_to_null(canonical_name); - canonical_type = empty_to_null(canonical_type); + const char *canonical_name = empty_to_null(reply.canonical.name); + const char *canonical_type = empty_to_null(reply.canonical.type); + const char *canonical_domain = reply.canonical.domain; if (!streq_ptr(name, canonical_name) || !streq_ptr(type, canonical_type) || @@ -1008,7 +956,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons printf("%s)\n", canonical_domain); } - print_source(flags, ts); + print_source(reply.flags, ts); return 0; } @@ -1016,29 +964,21 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons VERB(verb_service, "service", "[[NAME] TYPE] DOMAIN", 2, 4, 0, "Resolve service (SRV)"); static int verb_service(int argc, char *argv[], uintptr_t _data, void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - r = acquire_bus(&bus); - if (r < 0) - return r; - if (sd_json_format_enabled(arg_json_format_flags)) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format."); if (argc == 2) - return resolve_service(bus, NULL, NULL, argv[1]); + return resolve_service(NULL, NULL, argv[1]); else if (argc == 3) - return resolve_service(bus, NULL, argv[1], argv[2]); + return resolve_service(NULL, argv[1], argv[2]); else - return resolve_service(bus, argv[1], argv[2], argv[3]); + return resolve_service(argv[1], argv[2], argv[3]); } #if HAVE_OPENSSL -static int resolve_openpgp(sd_bus *bus, const char *address) { +static int resolve_openpgp(const char *address) { int r; - assert(bus); assert(address); const char *domain = strrchr(address, '@'); @@ -1069,7 +1009,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { log_debug("Looking up \"%s\".", full); r = resolve_record( - bus, full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_OPENPGPKEY, @@ -1089,7 +1028,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { log_debug("Looking up \"%s\".", full); return resolve_record( - bus, full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_OPENPGPKEY, @@ -1101,18 +1039,13 @@ VERB(verb_openpgp, "openpgp", "EMAIL@DOMAIN…", 2, VERB_ANY, 0, "Query OpenPGP public key"); static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) { #if HAVE_OPENSSL - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r, ret = 0; - - r = acquire_bus(&bus); - if (r < 0) - return r; + int ret = 0; if (sd_json_format_enabled(arg_json_format_flags)) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format."); STRV_FOREACH(p, strv_skip(argv, 1)) - RET_GATHER(ret, resolve_openpgp(bus, *p)); + RET_GATHER(ret, resolve_openpgp(*p)); return ret; #else @@ -1120,13 +1053,12 @@ static int verb_openpgp(int argc, char *argv[], uintptr_t _data, void *userdata) #endif } -static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) { +static int resolve_tlsa(const char *family, const char *address) { const char *port; uint16_t port_num = 443; _cleanup_free_ char *full = NULL; int r; - assert(bus); assert(address); port = strrchr(address, ':'); @@ -1147,7 +1079,7 @@ static int resolve_tlsa(sd_bus *bus, const char *family, const char *address) { log_debug("Looking up \"%s\".", full); - return resolve_record(bus, full, + return resolve_record(full, arg_class ?: DNS_CLASS_IN, arg_type ?: DNS_TYPE_TLSA, true); } @@ -1159,17 +1091,12 @@ static bool service_family_is_valid(const char *s) { VERB(verb_tlsa, "tlsa", "DOMAIN[:PORT]…", 2, VERB_ANY, 0, "Query TLS public key"); static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *family = "tcp"; char **args; - int r, ret = 0; + int ret = 0; assert(argc >= 2); - r = acquire_bus(&bus); - if (r < 0) - return r; - if (sd_json_format_enabled(arg_json_format_flags)) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format."); @@ -1180,7 +1107,7 @@ static int verb_tlsa(int argc, char *argv[], uintptr_t _data, void *userdata) { args = strv_skip(argv, 1); STRV_FOREACH(p, args) - RET_GATHER(ret, resolve_tlsa(bus, family, *p)); + RET_GATHER(ret, resolve_tlsa(family, *p)); return ret; } @@ -2386,7 +2313,7 @@ static int dump_cache_scope(sd_json_variant *scope) { static const sd_json_dispatch_field dispatch_table[] = { { "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, protocol), SD_JSON_MANDATORY }, - { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(struct scope_info, family), 0 }, + { "family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(struct scope_info, family), SD_JSON_RELAX }, { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(struct scope_info, ifindex), SD_JSON_RELAX }, { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct scope_info, ifname), 0 }, { "cache", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(struct scope_info, cache), SD_JSON_MANDATORY }, diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index 2ac4ee9555..2617b62775 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -135,7 +135,8 @@ static int reply_query_state(DnsQuery *q) { /* 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_varlink_errorbo(q->varlink_request, "io.systemd.Resolve.DNSError", - SD_JSON_BUILD_PAIR_INTEGER("rcode", DNS_RCODE_NXDOMAIN)); + SD_JSON_BUILD_PAIR_INTEGER("rcode", DNS_RCODE_NXDOMAIN), + JSON_BUILD_PAIR_STRING_NON_EMPTY("queryString", dns_query_string(q))); case DNS_TRANSACTION_RCODE_FAILURE: return sd_varlink_errorbo(q->varlink_request, "io.systemd.Resolve.DNSError", @@ -143,7 +144,8 @@ static int reply_query_state(DnsQuery *q) { SD_JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0, "extendedDNSErrorCode", SD_JSON_BUILD_INTEGER(q->answer_ede_rcode)), SD_JSON_BUILD_PAIR_CONDITION(q->answer_ede_rcode >= 0 && !isempty(q->answer_ede_msg), - "extendedDNSErrorMessage", SD_JSON_BUILD_STRING(q->answer_ede_msg))); + "extendedDNSErrorMessage", SD_JSON_BUILD_STRING(q->answer_ede_msg)), + JSON_BUILD_PAIR_STRING_NON_EMPTY("queryString", dns_query_string(q))); case DNS_TRANSACTION_NULL: case DNS_TRANSACTION_PENDING: @@ -346,7 +348,7 @@ static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *paramet static const sd_json_dispatch_field dispatch_table[] = { { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX }, { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, name), SD_JSON_MANDATORY }, - { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), 0 }, + { "family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(LookupParameters, family), SD_JSON_RELAX }, { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, {} }; @@ -377,9 +379,6 @@ static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *paramet if (r == 0) return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("name")); - if (!IN_SET(p.family, AF_UNSPEC, AF_INET, AF_INET6)) - return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); - if (validate_and_mangle_query_flags(m, &p.flags, p.name, SD_RESOLVED_NO_SEARCH) < 0) return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); @@ -475,7 +474,7 @@ finish: static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { static const sd_json_dispatch_field dispatch_table[] = { { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX }, - { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), SD_JSON_MANDATORY }, + { "family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(LookupParameters, family), SD_JSON_MANDATORY }, { "address", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(LookupParameters, address), SD_JSON_MANDATORY }, { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 }, {} @@ -501,9 +500,6 @@ static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *paramete if (r != 0) return r; - if (!IN_SET(p.family, AF_INET, AF_INET6)) - return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); - if (FAMILY_ADDRESS_SIZE(p.family) != p.address.iov_len) return sd_varlink_error(link, "io.systemd.Resolve.BadAddressSize", NULL); @@ -961,7 +957,7 @@ static int vl_method_resolve_service(sd_varlink* link, sd_json_variant* paramete { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParametersResolveService, type), 0 }, { "domain", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParametersResolveService, domain), SD_JSON_MANDATORY }, { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParametersResolveService, ifindex), SD_JSON_RELAX }, - { "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParametersResolveService, family), 0 }, + { "family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(LookupParametersResolveService, family), SD_JSON_RELAX }, { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParametersResolveService, flags), 0 }, {} }; @@ -987,9 +983,6 @@ static int vl_method_resolve_service(sd_varlink* link, sd_json_variant* paramete if (r != 0) return r; - if (!IN_SET(p.family, AF_INET, AF_INET6, AF_UNSPEC)) - return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family")); - if (isempty(p.name)) p.name = NULL; else if (!dns_service_name_is_valid(p.name)) diff --git a/src/shared/dns-configuration.c b/src/shared/dns-configuration.c index 1194e87258..01b59bbd89 100644 --- a/src/shared/dns-configuration.c +++ b/src/shared/dns-configuration.c @@ -36,7 +36,7 @@ static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_js static const sd_json_dispatch_field dns_server_dispatch_table[] = { { "address", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(DNSServer, addr), SD_JSON_MANDATORY }, { "addressString", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, - { "family", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSServer, family), SD_JSON_MANDATORY }, + { "family", SD_JSON_VARIANT_INTEGER, json_dispatch_address_family, offsetof(DNSServer, family), SD_JSON_MANDATORY }, { "port", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint16, offsetof(DNSServer, port), 0 }, { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSServer, ifindex), SD_JSON_RELAX }, { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSServer, server_name), 0 }, @@ -177,12 +177,12 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( static int dispatch_dns_scope(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { static const sd_json_dispatch_field dns_scope_dispatch_table[] = { - { "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, protocol), SD_JSON_MANDATORY }, - { "family", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, offsetof(DNSScope, family), 0 }, - { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, ifname), 0 }, - { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSScope, ifindex), SD_JSON_RELAX }, - { "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dnssec_mode_str), 0 }, - { "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dns_over_tls_mode_str), 0 }, + { "protocol", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, protocol), SD_JSON_MANDATORY }, + { "family", SD_JSON_VARIANT_INTEGER, json_dispatch_address_family, offsetof(DNSScope, family), SD_JSON_RELAX }, + { "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, ifname), 0 }, + { "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSScope, ifindex), SD_JSON_RELAX }, + { "dnssec", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dnssec_mode_str), 0 }, + { "dnsOverTLS", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSScope, dns_over_tls_mode_str), 0 }, {}, }; DNSScope **ret = ASSERT_PTR(userdata); diff --git a/src/shared/meson.build b/src/shared/meson.build index e2bef83bd2..def42815c7 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -185,6 +185,7 @@ shared_sources = files( 'resize-fs.c', 'resolve-hook-util.c', 'resolve-util.c', + 'resolve-varlink-util.c', 'rm-rf.c', 'seccomp-util.c', 'securebits-util.c', diff --git a/src/shared/resolve-varlink-util.c b/src/shared/resolve-varlink-util.c new file mode 100644 index 0000000000..779770a523 --- /dev/null +++ b/src/shared/resolve-varlink-util.c @@ -0,0 +1,376 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-json.h" + +#include "dns-packet.h" +#include "iovec-util.h" +#include "json-util.h" +#include "resolve-varlink-util.h" +#include "strv.h" + +void resolve_error_done(ResolveError *error) { + if (!error) + return; + + error->ede_msg = mfree(error->ede_msg); + error->query_string = mfree(error->query_string); + error->result = mfree(error->result); +} + +int dispatch_resolve_error(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_error_dispatch_table[] = { + { "rcode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(ResolveError, rcode), 0 }, + { "extendedDNSErrorCode", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(ResolveError, ede_rcode), 0 }, + { "extendedDNSErrorMessage", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolveError, ede_msg), 0 }, + { "queryString", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolveError, query_string), 0 }, + { "result", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolveError, result), 0 }, + {}, + }; + ResolveError *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_error_done) ResolveError error = { + .rcode = _DNS_RCODE_INVALID, + .ede_rcode = _DNS_EDE_RCODE_INVALID, + }; + r = sd_json_dispatch(variant, resolve_error_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &error); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(error); + return 0; +} + +static int dispatch_resolved_address(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_address_dispatch_table[] = { + { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(ResolvedAddress, ifindex), SD_JSON_RELAX }, + { "family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(ResolvedAddress, family), SD_JSON_MANDATORY }, + { "address", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr_data, offsetof(ResolvedAddress, in_addr), SD_JSON_MANDATORY }, + {}, + }; + ResolvedAddress *ret = ASSERT_PTR(userdata); + int r; + + ResolvedAddress address = {}; + r = sd_json_dispatch(variant, resolved_address_dispatch_table, flags & ~SD_JSON_MANDATORY, &address); + if (r < 0) + return r; + + if (address.family != address.in_addr.family) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "'%s' address and family are inconsistent.", strna(name)); + + *ret = TAKE_STRUCT(address); + return 0; +} + +static int dispatch_resolved_address_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolveHostnameReply *reply = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(reply->addresses, reply->n_addresses + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_address(name, v, flags, &reply->addresses[reply->n_addresses++]); + if (r < 0) + return r; + } + + return 0; +} + +void resolve_hostname_reply_done(ResolveHostnameReply *reply) { + if (!reply) + return; + + reply->name = mfree(reply->name); + reply->addresses = mfree(reply->addresses); + reply->n_addresses = 0; +} + +int dispatch_resolve_hostname_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_hostname_reply_dispatch_table[] = { + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolveHostnameReply, name), SD_JSON_MANDATORY }, + { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ResolveHostnameReply, flags), SD_JSON_MANDATORY }, + { "addresses", SD_JSON_VARIANT_ARRAY, dispatch_resolved_address_array, 0, SD_JSON_MANDATORY }, + {}, + }; + ResolveHostnameReply *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_hostname_reply_done) ResolveHostnameReply reply = {}; + r = sd_json_dispatch(variant, resolve_hostname_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(reply); + return 0; +} + +static void resolved_name_done(ResolvedName *name) { + if (!name) + return; + + name->name = mfree(name->name); +} + +static int dispatch_resolved_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_name_dispatch_table[] = { + { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(ResolvedName, ifindex), SD_JSON_RELAX }, + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedName, name), SD_JSON_MANDATORY }, + {}, + }; + ResolvedName *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolved_name_done) ResolvedName resolved_name = {}; + r = sd_json_dispatch(variant, resolved_name_dispatch_table, flags & ~SD_JSON_MANDATORY, &resolved_name); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(resolved_name); + return 0; +} + +static int dispatch_resolved_name_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolveAddressReply *reply = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(reply->names, reply->n_names + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_name(name, v, flags, &reply->names[reply->n_names++]); + if (r < 0) + return r; + } + + return 0; +} + +void resolve_address_reply_done(ResolveAddressReply *reply) { + if (!reply) + return; + + FOREACH_ARRAY(n, reply->names, reply->n_names) + resolved_name_done(n); + reply->names = mfree(reply->names); + reply->n_names = 0; +} + +int dispatch_resolve_address_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_address_reply_dispatch_table[] = { + { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ResolveAddressReply, flags), SD_JSON_MANDATORY }, + { "names", SD_JSON_VARIANT_ARRAY, dispatch_resolved_name_array, 0, SD_JSON_MANDATORY }, + {}, + }; + ResolveAddressReply *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_address_reply_done) ResolveAddressReply reply = {}; + r = sd_json_dispatch(variant, resolve_address_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(reply); + return 0; +} + +static void resolved_record_done(ResolvedRecord *record) { + if (!record) + return; + + iovec_done(&record->raw); +} + +static int dispatch_resolved_record(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_record_dispatch_table[] = { + { "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(ResolvedRecord, ifindex), SD_JSON_RELAX }, + { "raw", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_unbase64_iovec, offsetof(ResolvedRecord, raw), SD_JSON_MANDATORY }, + { "rr", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 }, + {}, + }; + ResolvedRecord *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolved_record_done) ResolvedRecord record = {}; + r = sd_json_dispatch(variant, resolved_record_dispatch_table, flags & ~SD_JSON_MANDATORY, &record); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(record); + return 0; +} + +static int dispatch_resolved_record_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolveRecordReply *reply = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(reply->records, reply->n_records + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_record(name, v, flags, &reply->records[reply->n_records++]); + if (r < 0) + return r; + } + + return 0; +} + +void resolve_record_reply_done(ResolveRecordReply *reply) { + if (!reply) + return; + + FOREACH_ARRAY(record, reply->records, reply->n_records) + resolved_record_done(record); + reply->records = mfree(reply->records); + reply->n_records = 0; +} + +int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_record_reply_dispatch_table[] = { + { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ResolveRecordReply, flags), SD_JSON_MANDATORY }, + { "rrs", SD_JSON_VARIANT_ARRAY, dispatch_resolved_record_array, 0, SD_JSON_MANDATORY }, + {}, + }; + ResolveRecordReply *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_record_reply_done) ResolveRecordReply reply = {}; + r = sd_json_dispatch(variant, resolve_record_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(reply); + return 0; +} + +static void resolved_service_done(ResolvedService *service) { + if (!service) + return; + + service->hostname = mfree(service->hostname); + service->canonical_name = mfree(service->canonical_name); + service->addresses = mfree(service->addresses); + service->n_addresses = 0; +} + +static int dispatch_resolved_service_address_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolvedService *service = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(service->addresses, service->n_addresses + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_address(name, v, flags, &service->addresses[service->n_addresses++]); + if (r < 0) + return r; + } + + return 0; +} + +static int dispatch_resolved_service(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_service_dispatch_table[] = { + { "priority", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(ResolvedService, priority), SD_JSON_MANDATORY }, + { "weight", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(ResolvedService, weight), SD_JSON_MANDATORY }, + { "port", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(ResolvedService, port), SD_JSON_MANDATORY }, + { "hostname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedService, hostname), SD_JSON_MANDATORY }, + { "canonicalName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedService, canonical_name), SD_JSON_NULLABLE }, + { "addresses", SD_JSON_VARIANT_ARRAY, dispatch_resolved_service_address_array, 0, SD_JSON_NULLABLE }, + {}, + }; + ResolvedService *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolved_service_done) ResolvedService service = {}; + r = sd_json_dispatch(variant, resolved_service_dispatch_table, flags & ~SD_JSON_MANDATORY, &service); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(service); + return 0; +} + +static int dispatch_resolved_service_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + ResolveServiceReply *reply = ASSERT_PTR(userdata); + int r; + + sd_json_variant *v; + JSON_VARIANT_ARRAY_FOREACH(v, variant) { + if (!GREEDY_REALLOC0(reply->services, reply->n_services + 1)) + return json_log_oom(variant, flags); + + r = dispatch_resolved_service(name, v, flags, &reply->services[reply->n_services++]); + if (r < 0) + return r; + } + + return 0; +} + +static void resolved_canonical_done(ResolvedCanonical *canonical) { + if (!canonical) + return; + + canonical->name = mfree(canonical->name); + canonical->type = mfree(canonical->type); + canonical->domain = mfree(canonical->domain); +} + +static int dispatch_resolved_canonical(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolved_canonical_dispatch_table[] = { + { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, name), SD_JSON_NULLABLE }, + { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, type), SD_JSON_MANDATORY }, + { "domain", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ResolvedCanonical, domain), SD_JSON_MANDATORY }, + {}, + }; + ResolvedCanonical *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolved_canonical_done) ResolvedCanonical canonical = {}; + r = sd_json_dispatch(variant, resolved_canonical_dispatch_table, flags & ~SD_JSON_MANDATORY, &canonical); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(canonical); + return 0; +} + +void resolve_service_reply_done(ResolveServiceReply *reply) { + if (!reply) + return; + + FOREACH_ARRAY(service, reply->services, reply->n_services) + resolved_service_done(service); + reply->services = mfree(reply->services); + reply->n_services = 0; + reply->txt = strv_free(reply->txt); + resolved_canonical_done(&reply->canonical); +} + +int dispatch_resolve_service_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + static const sd_json_dispatch_field resolve_service_reply_dispatch_table[] = { + { "services", SD_JSON_VARIANT_ARRAY, dispatch_resolved_service_array, 0, SD_JSON_MANDATORY }, + { "txt", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(ResolveServiceReply, txt), SD_JSON_NULLABLE }, + { "canonical", SD_JSON_VARIANT_OBJECT, dispatch_resolved_canonical, offsetof(ResolveServiceReply, canonical), SD_JSON_MANDATORY }, + { "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ResolveServiceReply, flags), SD_JSON_MANDATORY }, + {}, + }; + ResolveServiceReply *ret = ASSERT_PTR(userdata); + int r; + + _cleanup_(resolve_service_reply_done) ResolveServiceReply reply = {}; + r = sd_json_dispatch(variant, resolve_service_reply_dispatch_table, flags | SD_JSON_ALLOW_EXTENSIONS, &reply); + if (r < 0) + return r; + + *ret = TAKE_STRUCT(reply); + return 0; +} diff --git a/src/shared/resolve-varlink-util.h b/src/shared/resolve-varlink-util.h new file mode 100644 index 0000000000..a05e18daea --- /dev/null +++ b/src/shared/resolve-varlink-util.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "in-addr-util.h" +#include "shared-forward.h" + +typedef struct ResolveError { + int rcode; + int ede_rcode; + char *ede_msg; + char *query_string; + char *result; +} ResolveError; + +void resolve_error_done(ResolveError *error); + +int dispatch_resolve_error(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); + +typedef struct ResolvedAddress { + int ifindex; + int family; + struct in_addr_data in_addr; +} ResolvedAddress; + +typedef struct ResolveHostnameReply { + char *name; + uint64_t flags; + ResolvedAddress *addresses; + size_t n_addresses; +} ResolveHostnameReply; + +void resolve_hostname_reply_done(ResolveHostnameReply *reply); + +int dispatch_resolve_hostname_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); + +typedef struct ResolvedName { + int ifindex; + char *name; +} ResolvedName; + +typedef struct ResolveAddressReply { + uint64_t flags; + ResolvedName *names; + size_t n_names; +} ResolveAddressReply; + +void resolve_address_reply_done(ResolveAddressReply *reply); + +int dispatch_resolve_address_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); + +typedef struct ResolvedRecord { + int ifindex; + struct iovec raw; +} ResolvedRecord; + +typedef struct ResolveRecordReply { + uint64_t flags; + ResolvedRecord *records; + size_t n_records; +} ResolveRecordReply; + +void resolve_record_reply_done(ResolveRecordReply *reply); + +int dispatch_resolve_record_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); + +typedef struct ResolvedService { + uint16_t priority; + uint16_t weight; + uint16_t port; + char *hostname; + char *canonical_name; + ResolvedAddress *addresses; + size_t n_addresses; +} ResolvedService; + +typedef struct ResolvedCanonical { + char *name; + char *type; + char *domain; +} ResolvedCanonical; + +typedef struct ResolveServiceReply { + ResolvedService *services; + size_t n_services; + char **txt; + ResolvedCanonical canonical; + uint64_t flags; +} ResolveServiceReply; + +void resolve_service_reply_done(ResolveServiceReply *reply); + +int dispatch_resolve_service_reply(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); diff --git a/src/shared/varlink-io.systemd.Resolve.c b/src/shared/varlink-io.systemd.Resolve.c index e8de8b39f8..6c4ef094ca 100644 --- a/src/shared/varlink-io.systemd.Resolve.c +++ b/src/shared/varlink-io.systemd.Resolve.c @@ -337,6 +337,7 @@ static SD_VARLINK_DEFINE_ERROR(StubLoop); static SD_VARLINK_DEFINE_ERROR( DNSError, SD_VARLINK_DEFINE_FIELD(rcode, SD_VARLINK_INT, 0), + SD_VARLINK_DEFINE_FIELD(queryString, SD_VARLINK_STRING, 0), SD_VARLINK_DEFINE_FIELD(extendedDNSErrorCode, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(extendedDNSErrorMessage, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ERROR(CNAMELoop); diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh index abc6b0d8ac..d9757b3855 100755 --- a/test/units/TEST-75-RESOLVED.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -770,7 +770,7 @@ testcase_08_resolved() { grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT" (! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}') grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" - grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT" + grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!","queryString":"edns-extra-text.forwarded.test"}' "$RUN_OUT" journalctl --sync journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)" @@ -779,7 +779,7 @@ testcase_08_resolved() { grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT" (! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}') grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" - grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT" + grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱","queryString":"edns-code-zero.forwarded.test"}' "$RUN_OUT" journalctl --sync journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)" @@ -788,7 +788,7 @@ testcase_08_resolved() { grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT" (! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}') grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" - grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT" + grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"queryString":"edns-invalid-code.forwarded.test"}' "$RUN_OUT" journalctl --sync journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)" @@ -797,7 +797,7 @@ testcase_08_resolved() { grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT" (! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}') grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" - grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT" + grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World","queryString":"edns-invalid-code-with-extra-text.forwarded.test"}' "$RUN_OUT" journalctl --sync journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)" } @@ -1258,13 +1258,13 @@ testcase_14_refuse_record_types() { grep -qF "127.128.0.5" "$RUN_OUT" (! run resolvectl query localhost5 --type=SRV) - grep -qF "DNS query type refused." "$RUN_OUT" + grep -qF "DNS query type refused" "$RUN_OUT" (! run resolvectl query localhost5 --type=TXT) - grep -qF "DNS query type refused." "$RUN_OUT" + grep -qF "DNS query type refused" "$RUN_OUT" (! run resolvectl query localhost5 --type=AAAA) - grep -qF "DNS query type refused." "$RUN_OUT" + grep -qF "DNS query type refused" "$RUN_OUT" run resolvectl query localhost5 --type=A grep -qF "127.128.0.5" "$RUN_OUT" @@ -1304,7 +1304,7 @@ testcase_14_refuse_record_types() { grep -qF "does not have any RR of the requested type" "$RUN_OUT" (! run resolvectl query localhost5 --type=AAAA) - grep -qF "DNS query type refused." "$RUN_OUT" + grep -qF "DNS query type refused" "$RUN_OUT" run resolvectl service _mysvc._tcp signed.test grep -qF "myservice.signed.test:1234" "$RUN_OUT"