Compare commits

..

13 Commits

Author SHA1 Message Date
PhoenixDiscord e8607daf7d
Replace gendered pronouns with gender neutral ones. (#16844) 2020-08-27 11:52:48 +09:00
Chris Down 33e1a5d8d3 path: Improve $PATH search directory case
Previously:

1. last_error wouldn't be updated with errors from is_dir;
2. We'd always issue a stat(), even for binaries without execute;
3. We used stat() instead of access(), which is cheaper.

This change avoids all of those, by only checking inside X_OK-positive
case whether access() works on the path with an extra slash appended.
Thanks to Lennart for the suggestion.
2020-08-27 00:52:20 +01:00
Zbigniew Jędrzejewski-Szmek bb2aee7d11
Merge pull request #16757 from poettering/nss-resolve-varlink
resolved: use varlink for communication between nss-resolve and resolved
2020-08-26 22:07:34 +02:00
Lennart Poettering 0c73f4f075 nss-resolve: port over to new varlink interface 2020-08-26 16:48:16 +02:00
Lennart Poettering 9581bb8424 resolved: add minimal varlink api for resolving hostnames/addresses
This allows us to later port nss-resolve to use Varlink rather than
D-Bus for resolution. This has the benefit that nss-resolve based
resoluton works even without D-Bus being up. And it's faster too.
2020-08-26 16:48:06 +02:00
Lennart Poettering c17b5ce739 resolved: minor clean-ups for resolved-bus.c 2020-08-26 16:48:02 +02:00
Lennart Poettering 65a01e8242 resolved: move query bus tracking to resolved-bus.c
It's strictly bus-specific, hence let's move this to resolved-bus.c like
the rest of the bus specific logic.

This is also in preparation for adding an alternative varlink transport,
which needs similar functionality, but varlink instead of bus-specific.
2020-08-26 16:47:57 +02:00
Lennart Poettering c9de4e0f5b resolved: rename request → bus_request
Let's prepare for adding a new varlink interface, and thus rename the
"request" field to "bus_request", so that we can later add a
varlink_request field too.
2020-08-26 16:47:53 +02:00
Lennart Poettering d4f72f0d21 resolved: drop suppress_unroutable_family field
It's unused since 90bdc8be66.
2020-08-26 16:47:49 +02:00
Lennart Poettering b97e3e3d7a json: also add explicit dispatchers for 'int' and 'unsigned' 2020-08-26 16:47:32 +02:00
Lennart Poettering 0710343ce7 json: add support for byte arrays to json builder 2020-08-26 16:47:16 +02:00
Lennart Poettering 7466e94f13 varlink: add helper for generating errno errors 2020-08-26 16:46:57 +02:00
Lennart Poettering c7dfa1df8b in-addr-util: add byte accessor array to union in_addr_union
It's pretty useful to be able to access the bytes generically, without
acknowledging a specific family, hence let's a third way to access an
in_addr_union.
2020-08-26 16:46:12 +02:00
23 changed files with 1062 additions and 406 deletions

View File

@ -196,8 +196,8 @@ The currently used part of the file is the **header_size** plus the
**arena_size** field of the header. If a writer needs to write to a file where **arena_size** field of the header. If a writer needs to write to a file where
the actual file size on disk is smaller than the reported value it shall the actual file size on disk is smaller than the reported value it shall
immediately rotate the file and start a new one. If a writer is asked to write immediately rotate the file and start a new one. If a writer is asked to write
to a file with a header that is shorter than his own definition of the struct to a file with a header that is shorter than its own definition of the struct
Header, he shall immediately rotate the file and start a new one. Header, it shall immediately rotate the file and start a new one.
The **n_objects** field contains a counter for objects currently available in The **n_objects** field contains a counter for objects currently available in
this file. As objects are appended to the end of the file this counter is this file. As objects are appended to the end of the file this counter is

View File

@ -12,6 +12,7 @@
union in_addr_union { union in_addr_union {
struct in_addr in; struct in_addr in;
struct in6_addr in6; struct in6_addr in6;
uint8_t bytes[CONST_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))];
}; };
struct in_addr_data { struct in_addr_data {

View File

@ -21,7 +21,7 @@ struct LookupPaths {
char **search_path; char **search_path;
/* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin
* shall place his own unit files. */ * shall place their own unit files. */
char *persistent_config; char *persistent_config;
char *runtime_config; char *runtime_config;

View File

@ -638,17 +638,28 @@ int find_binary(const char *name, char **ret) {
if (!j) if (!j)
return -ENOMEM; return -ENOMEM;
if (is_dir(j, true)) if (access(j, X_OK) >= 0) {
_cleanup_free_ char *with_dash;
with_dash = strjoin(j, "/");
if (!with_dash)
return -ENOMEM;
/* If this passes, it must be a directory, and so should be skipped. */
if (access(with_dash, X_OK) >= 0)
continue; continue;
if (access(j, X_OK) >= 0) { /**
* We can't just `continue` inverting this case, since we need to update last_error.
*/
if (errno == ENOTDIR) {
/* Found it! */ /* Found it! */
if (ret) if (ret)
*ret = path_simplify(TAKE_PTR(j), false); *ret = path_simplify(TAKE_PTR(j), false);
return 0; return 0;
} }
}
/* PATH entries which we don't have access to are ignored, as per tradition. */ /* PATH entries which we don't have access to are ignored, as per tradition. */
if (errno != EACCES) if (errno != EACCES)

View File

@ -65,7 +65,7 @@ int procfs_tasks_set_limit(uint64_t limit) {
if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live, if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live,
* hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero
* limit he/she probably means "no limit", hence let's better refuse this to avoid * limit they probably mean "no limit", hence let's better refuse this to avoid
* confusion. */ * confusion. */
return -EINVAL; return -EINVAL;

View File

@ -412,7 +412,7 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
if (connect(fd, &sa.sa, sa_len) < 0) if (connect(fd, &sa.sa, sa_len) < 0)
return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
* indication that his wasn't an AF_UNIX socket after all */ * indication that this wasn't an AF_UNIX socket after all */
if ((flags & O_ACCMODE) == O_RDONLY) if ((flags & O_ACCMODE) == O_RDONLY)
r = shutdown(fd, SHUT_WR); r = shutdown(fd, SHUT_WR);

View File

@ -1337,7 +1337,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom(); return log_oom();
/* If the user asked for a particular /* If the user asked for a particular
* property, show it to him, even if it is * property, show it to them, even if it is
* empty. */ * empty. */
arg_all = true; arg_all = true;
break; break;

View File

@ -2672,7 +2672,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom(); return log_oom();
/* If the user asked for a particular /* If the user asked for a particular
* property, show it to him, even if it is * property, show it to them, even if it is
* empty. */ * empty. */
arg_all = true; arg_all = true;
break; break;

View File

@ -7,10 +7,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "sd-bus.h"
#include "bus-common-errors.h"
#include "bus-locator.h"
#include "errno-util.h" #include "errno-util.h"
#include "in-addr-util.h" #include "in-addr-util.h"
#include "macro.h" #include "macro.h"
@ -18,69 +14,36 @@
#include "resolved-def.h" #include "resolved-def.h"
#include "signal-util.h" #include "signal-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h"
#include "varlink.h"
NSS_GETHOSTBYNAME_PROTOTYPES(resolve); NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
NSS_GETHOSTBYADDR_PROTOTYPES(resolve); NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
static bool bus_error_shall_fallback(sd_bus_error *e) { static bool error_shall_fallback(const char *error_id) {
return sd_bus_error_has_names(e, return STR_IN_SET(error_id,
SD_BUS_ERROR_SERVICE_UNKNOWN, VARLINK_ERROR_DISCONNECTED,
SD_BUS_ERROR_NAME_HAS_NO_OWNER, VARLINK_ERROR_TIMEOUT,
SD_BUS_ERROR_NO_REPLY, VARLINK_ERROR_PROTOCOL,
SD_BUS_ERROR_ACCESS_DENIED, VARLINK_ERROR_INTERFACE_NOT_FOUND,
SD_BUS_ERROR_DISCONNECTED, VARLINK_ERROR_METHOD_NOT_FOUND,
SD_BUS_ERROR_TIMEOUT, VARLINK_ERROR_METHOD_NOT_IMPLEMENTED);
BUS_ERROR_NO_SUCH_UNIT);
} }
static int count_addresses(sd_bus_message *m, int af, const char **canonical) { static int connect_to_resolved(Varlink **ret) {
int c = 0, r; _cleanup_(varlink_unrefp) Varlink *link = NULL;
int r;
assert(m); r = varlink_connect_address(&link, "/run/systemd/resolve/io.systemd.Resolve");
assert(canonical);
r = sd_bus_message_enter_container(m, 'a', "(iiay)");
if (r < 0) if (r < 0)
return r; return r;
while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { r = varlink_set_relative_timeout(link, SD_RESOLVED_QUERY_TIMEOUT_USEC);
int family, ifindex;
assert_cc(sizeof(int32_t) == sizeof(int));
r = sd_bus_message_read(m, "ii", &ifindex, &family);
if (r < 0) if (r < 0)
return r; return r;
r = sd_bus_message_skip(m, "ay"); *ret = TAKE_PTR(link);
if (r < 0) return 0;
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
if (af != AF_UNSPEC && family != af)
continue;
c++;
}
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;
} }
static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) { 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; 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 assert(variant);
* code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to assert(ifi);
* 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. */
if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */ if (!json_variant_is_integer(variant))
return false; 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") && t = json_variant_integer(variant);
streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system"); 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( enum nss_status _nss_resolve_gethostbyname4_r(
const char *name, const char *name,
struct gaih_addrtuple **pat, struct gaih_addrtuple **pat,
@ -118,14 +172,15 @@ enum nss_status _nss_resolve_gethostbyname4_r(
int *errnop, int *h_errnop, int *errnop, int *h_errnop,
int32_t *ttlp) { int32_t *ttlp) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL; struct gaih_addrtuple *r_tuple = NULL, *r_tuple_first = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(varlink_unrefp) Varlink *link = NULL;
const char *canonical = NULL; const char *canonical = NULL, *error_id = NULL;
size_t l, ms, idx; JsonVariant *entry, *rparams;
size_t l, ms, idx, c = 0;
char *r_name; char *r_name;
int c, r, i = 0; int r;
PROTECT_ERRNO; PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
@ -136,50 +191,53 @@ enum nss_status _nss_resolve_gethostbyname4_r(
assert(errnop); assert(errnop);
assert(h_errnop); assert(h_errnop);
if (avoid_deadlock()) { r = connect_to_resolved(&link);
r = -EDEADLK;
goto fail;
}
r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; 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) if (r < 0)
goto fail; goto fail;
r = sd_bus_message_set_auto_start(req, false); r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
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);
if (r < 0) { if (r < 0) {
if (!bus_error_shall_fallback(&error)) if (!error_shall_fallback(error_id))
goto not_found; goto not_found;
/* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, allowing falling
allowing falling back to other nss modules. Treat all other error conditions as back to other nss modules. Treat all other error conditions as NOTFOUND. This includes
NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf
case so that the nsswitch.conf configuration can distinguish such executed but configuration can distinguish such executed but negative replies from complete failure to
negative replies from complete failure to talk to resolved). */ talk to resolved). */
goto fail; goto fail;
} }
c = count_addresses(reply, AF_UNSPEC, &canonical); r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, 0, &p);
if (c < 0) { if (r < 0)
r = c;
goto fail; goto fail;
} if (json_variant_is_blank_object(p.addresses))
if (c == 0)
goto not_found; goto not_found;
if (isempty(canonical)) JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
canonical = name; 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); l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
@ -198,56 +256,29 @@ enum nss_status _nss_resolve_gethostbyname4_r(
/* Second, append addresses */ /* Second, append addresses */
r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q);
if (r < 0) if (r < 0)
goto fail; goto fail;
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { if (!IN_SET(q.family, AF_INET, AF_INET6))
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);
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))
continue; continue;
if (sz != FAMILY_ADDRESS_SIZE(family)) {
r = -EINVAL;
goto fail;
}
r_tuple = (struct gaih_addrtuple*) (buffer + idx); 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->name = r_name;
r_tuple->family = family; r_tuple->family = q.family;
r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex); r_tuple->scopeid = ifindex_to_scopeid(q.family, &q.address, q.ifindex);
memcpy(r_tuple->addr, a, sz); memcpy(r_tuple->addr, &q.address, q.address_size);
idx += ALIGN(sizeof(struct gaih_addrtuple)); 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); assert(idx == ms);
if (*pat) if (*pat)
@ -285,13 +316,14 @@ enum nss_status _nss_resolve_gethostbyname3_r(
int32_t *ttlp, int32_t *ttlp,
char **canonp) { char **canonp) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list; 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;
size_t l, idx, ms, alen; const char *canonical, *error_id = NULL;
const char *canonical; size_t l, idx, ms, alen, i = 0, c = 0;
int c, r, i = 0; JsonVariant *entry, *rparams;
int r;
PROTECT_ERRNO; PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
@ -310,50 +342,53 @@ enum nss_status _nss_resolve_gethostbyname3_r(
goto fail; goto fail;
} }
if (avoid_deadlock()) { r = connect_to_resolved(&link);
r = -EDEADLK;
goto fail;
}
r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; 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) if (r < 0)
goto fail; goto fail;
r = sd_bus_message_set_auto_start(req, false); r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
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);
if (r < 0) { if (r < 0) {
if (!bus_error_shall_fallback(&error)) if (!error_shall_fallback(error_id))
goto not_found; goto not_found;
goto fail; goto fail;
} }
c = count_addresses(reply, af, &canonical); r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, 0, &p);
if (c < 0) { if (r < 0)
r = c;
goto fail; goto fail;
} if (json_variant_is_blank_object(p.addresses))
if (c == 0)
goto not_found; goto not_found;
if (isempty(canonical)) JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
canonical = name; 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); alen = FAMILY_ADDRESS_SIZE(af);
l = strlen(canonical); 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) { if (buflen < ms) {
UNPROTECT_ERRNO; UNPROTECT_ERRNO;
@ -375,45 +410,24 @@ enum nss_status _nss_resolve_gethostbyname3_r(
/* Third, append addresses */ /* Third, append addresses */
r_addr = buffer + idx; r_addr = buffer + idx;
r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
r = json_dispatch(entry, address_parameters_dispatch_table, NULL, 0, &q);
if (r < 0) if (r < 0)
goto fail; goto fail;
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { if (q.family != af)
int ifindex, family;
const void *a;
size_t sz;
r = sd_bus_message_read(reply, "ii", &ifindex, &family);
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)
continue; continue;
if (sz != alen) { if (q.address_size != alen) {
r = -EINVAL; r = -EINVAL;
goto fail; goto fail;
} }
memcpy(r_addr + i*ALIGN(alen), a, alen); memcpy(r_addr + i*ALIGN(alen), &q.address, alen);
i++; i++;
} }
if (r < 0)
goto fail;
assert(i == c); assert(i == c);
idx += c * ALIGN(alen); idx += c * ALIGN(alen);
@ -458,6 +472,40 @@ not_found:
return NSS_STATUS_NOTFOUND; 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( enum nss_status _nss_resolve_gethostbyaddr2_r(
const void* addr, socklen_t len, const void* addr, socklen_t len,
int af, int af,
@ -466,14 +514,15 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
int *errnop, int *h_errnop, int *errnop, int *h_errnop,
int32_t *ttlp) { int32_t *ttlp) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
char *r_name, *r_aliases, *r_addr, *r_addr_list; 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; unsigned c = 0, i = 0;
size_t ms = 0, idx; size_t ms = 0, idx;
const char *n; int r;
int r, ifindex;
PROTECT_ERRNO; PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
@ -496,70 +545,42 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
goto fail; goto fail;
} }
if (avoid_deadlock()) { r = connect_to_resolved(&link);
r = -EDEADLK;
goto fail;
}
r = sd_bus_open_system(&bus);
if (r < 0) if (r < 0)
goto fail; 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) if (r < 0)
goto fail; goto fail;
r = sd_bus_message_set_auto_start(req, false); r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL);
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);
if (r < 0) { if (r < 0) {
if (!bus_error_shall_fallback(&error)) if (!error_shall_fallback(error_id))
goto not_found; goto not_found;
goto fail; 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) if (r < 0)
goto fail; goto fail;
if (json_variant_is_blank_object(p.names))
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)
goto not_found; goto not_found;
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 */ ms += ALIGN(len) + /* the address */
2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ json_variant_elements(p.names) * sizeof(char*); /* pointers to aliases, plus trailing NULL */
if (buflen < ms) { if (buflen < ms) {
UNPROTECT_ERRNO; UNPROTECT_ERRNO;
@ -586,22 +607,25 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
/* Fourth, place aliases */ /* Fourth, place aliases */
i = 0; i = 0;
r_name = buffer + idx; r_name = buffer + idx;
while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
char *p; _cleanup_(name_parameters_destroy) NameParameters q = {};
size_t l; size_t l;
char *z;
l = strlen(n); r = json_dispatch(entry, name_parameters_dispatch_table, NULL, 0, &q);
p = buffer + idx; if (r < 0)
memcpy(p, n, l+1); goto fail;
l = strlen(q.name);
z = buffer + idx;
memcpy(z, n, l+1);
if (i > 0) if (i > 0)
((char**) r_aliases)[i-1] = p; ((char**) r_aliases)[i-1] = z;
i++; i++;
idx += ALIGN(l+1); idx += ALIGN(l+1);
} }
if (r < 0)
goto fail;
((char**) r_aliases)[c-1] = NULL; ((char**) r_aliases)[c-1] = NULL;
assert(idx == ms); assert(idx == ms);

View File

@ -17,53 +17,55 @@ basic_dns_sources = files('''
dns_type_h = files('dns-type.h')[0] dns_type_h = files('dns-type.h')[0]
systemd_resolved_sources = files(''' 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.c
resolved-bus.h resolved-bus.h
resolved-link.h resolved-conf.c
resolved-link.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.c
resolved-link-bus.h resolved-link-bus.h
resolved-llmnr.h resolved-link.c
resolved-link.h
resolved-llmnr.c resolved-llmnr.c
resolved-mdns.h resolved-llmnr.h
resolved-manager.c
resolved-manager.h
resolved-mdns.c resolved-mdns.c
resolved-def.h resolved-mdns.h
resolved-dns-query.h resolved-resolv-conf.c
resolved-dns-query.c resolved-resolv-conf.h
resolved-dns-synthesize.h resolved-varlink.c
resolved-dns-synthesize.c resolved-varlink.h
resolved-dns-transaction.h resolved.c
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
'''.split()) '''.split())
resolvectl_sources = files(''' resolvectl_sources = files('''

View File

@ -24,45 +24,81 @@
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_resolve_support, resolve_support, ResolveSupport); 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) { static int reply_query_state(DnsQuery *q) {
assert(q);
assert(q->bus_request);
switch (q->state) { switch (q->state) {
case DNS_TRANSACTION_NO_SERVERS: 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: 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: 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: 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: 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: 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: 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)); dnssec_result_to_string(q->answer_dnssec_result));
case DNS_TRANSACTION_NO_TRUST_ANCHOR: 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: 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: 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: case DNS_TRANSACTION_NOT_FOUND:
/* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we /* 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. */ * 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: { case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _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); 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: 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_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *normalized = NULL; _cleanup_free_ char *normalized = NULL;
DnsQuestion *question;
DnsResourceRecord *rr; DnsResourceRecord *rr;
unsigned added = 0; unsigned added = 0;
int ifindex, r; int ifindex, r;
@ -152,7 +189,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
r = dns_query_process_cname(q); r = dns_query_process_cname(q);
if (r == -ELOOP) { 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; goto finish;
} }
if (r < 0) 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. */ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return; 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) if (r < 0)
goto finish; goto finish;
@ -168,11 +205,10 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0) if (r < 0)
goto finish; 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)); r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
if (r < 0) if (r < 0)
goto finish; goto finish;
@ -190,7 +226,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
} }
if (added <= 0) { 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; goto finish;
} }
@ -198,14 +234,14 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0) if (r < 0)
goto finish; goto finish;
/* The key names are not necessarily normalized, make sure that they are when we return them to our bus /* The key names are not necessarily normalized, make sure that they are when we return them to our
* clients. */ * bus clients. */
assert(canonical);
r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized);
if (r < 0) if (r < 0)
goto finish; goto finish;
/* Return the precise spelling and uppercasing and CNAME target reported by the server */ /* Return the precise spelling and uppercasing and CNAME target reported by the server */
assert(canonical);
r = sd_bus_message_append( r = sd_bus_message_append(
reply, "st", reply, "st",
normalized, normalized,
@ -218,15 +254,29 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
finish: finish:
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to send hostname reply: %m"); 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); 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); 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) if (ifindex < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); 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)) 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); 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) if (r < 0)
return r; return r;
@ -350,10 +400,9 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
if (r < 0) if (r < 0)
return r; return r;
q->request = sd_bus_message_ref(message); q->bus_request = sd_bus_message_ref(message);
q->request_family = family; q->request_family = family;
q->complete = bus_method_resolve_hostname_complete; q->complete = bus_method_resolve_hostname_complete;
q->suppress_unroutable_family = family == AF_UNSPEC;
r = dns_query_bus_track(q, message); r = dns_query_bus_track(q, message);
if (r < 0) if (r < 0)
@ -386,7 +435,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
r = dns_query_process_cname(q); r = dns_query_process_cname(q);
if (r == -ELOOP) { 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; goto finish;
} }
if (r < 0) 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. */ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return; 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) if (r < 0)
goto finish; goto finish;
@ -428,7 +477,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
_cleanup_free_ char *ip = NULL; _cleanup_free_ char *ip = NULL;
(void) in_addr_to_string(q->request_family, &q->request_address, &ip); (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)); "Address '%s' does not have any RR of requested type", strnull(ip));
goto finish; goto finish;
} }
@ -446,7 +495,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
finish: finish:
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to send address reply: %m"); 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); dns_query_free(q);
@ -478,7 +527,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0) if (r < 0)
return r; return r;
r = check_ifindex_flags(ifindex, &flags, 0, error); r = validate_and_mangle_ifindex_and_flags(ifindex, &flags, 0, error);
if (r < 0) if (r < 0)
return r; return r;
@ -490,7 +539,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0) if (r < 0)
return r; return r;
q->request = sd_bus_message_ref(message); q->bus_request = sd_bus_message_ref(message);
q->request_family = family; q->request_family = family;
q->request_address = a; q->request_address = a;
q->complete = bus_method_resolve_address_complete; 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); r = dns_query_process_cname(q);
if (r == -ELOOP) { 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; goto finish;
} }
if (r < 0) 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. */ if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return; 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) if (r < 0)
goto finish; goto finish;
@ -588,7 +637,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
} }
if (added <= 0) { 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; goto finish;
} }
@ -605,7 +654,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
finish: finish:
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to send record reply: %m"); 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); 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)) 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); 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) if (r < 0)
return r; return r;
@ -667,7 +716,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
* blob */ * blob */
q->clamp_ttl = true; 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; q->complete = bus_method_resolve_record_complete;
r = dns_query_bus_track(q, message); r = dns_query_bus_track(q, message);
@ -891,7 +940,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
assert(bad->auxiliary_result != 0); assert(bad->auxiliary_result != 0);
if (bad->auxiliary_result == -ELOOP) { 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; 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) if (r < 0)
goto finish; goto finish;
@ -913,6 +962,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
goto finish; goto finish;
question = dns_query_question_for_protocol(q, q->answer_protocol); question = dns_query_question_for_protocol(q, q->answer_protocol);
DNS_ANSWER_FOREACH(rr, q->answer) { DNS_ANSWER_FOREACH(rr, q->answer) {
r = dns_question_matches_rr(question, rr, NULL); r = dns_question_matches_rr(question, rr, NULL);
if (r < 0) if (r < 0)
@ -933,7 +983,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
} }
if (added <= 0) { 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; goto finish;
} }
@ -979,7 +1029,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
finish: finish:
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to send service reply: %m"); 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); dns_query_free(q);
@ -1071,7 +1121,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
r = dns_query_process_cname(q); r = dns_query_process_cname(q);
if (r == -ELOOP) { 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; goto finish;
} }
if (r < 0) if (r < 0)
@ -1115,12 +1165,12 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
* domain. Report this as a recognizable * domain. Report this as a recognizable
* error. See RFC 2782, Section "Usage * error. See RFC 2782, Section "Usage
* Rules". */ * 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; goto finish;
} }
if (found <= 0) { 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; goto finish;
} }
@ -1131,7 +1181,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
finish: finish:
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to send service reply: %m"); 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); dns_query_free(q);
@ -1177,7 +1227,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
if (name && !type) if (name && !type)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service 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) if (r < 0)
return r; return r;
@ -1193,7 +1243,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
if (r < 0) if (r < 0)
return r; return r;
q->request = sd_bus_message_ref(message); q->bus_request = sd_bus_message_ref(message);
q->request_family = family; q->request_family = family;
q->complete = bus_method_resolve_service_complete; 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); 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; DnssdService *s = userdata;
assert(t); assert(t);
@ -1863,7 +1913,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
if (r < 0) if (r < 0)
return r; 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) if (r < 0)
return r; return r;

View File

@ -338,9 +338,14 @@ DnsQuery *dns_query_free(DnsQuery *q) {
dns_query_reset_answer(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); 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->request_dns_packet);
dns_packet_unref(q->reply_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; return 0;
} }
static void dns_query_complete(DnsQuery *q, DnsTransactionState state) { void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
assert(q); assert(q);
assert(!DNS_TRANSACTION_IS_LIVE(state)); assert(!DNS_TRANSACTION_IS_LIVE(state));
assert(DNS_TRANSACTION_IS_LIVE(q->state)); assert(DNS_TRANSACTION_IS_LIVE(q->state));
/* Note that this call might invalidate the query. Callers /* Note that this call might invalidate the query. Callers should hence not attempt to access the
* should hence not attempt to access the query or transaction * query or transaction after calling this function. */
* after calling this function. */
q->state = state; 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 */ 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) { DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
assert(q); assert(q);

View File

@ -4,6 +4,7 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "set.h" #include "set.h"
#include "varlink.h"
typedef struct DnsQueryCandidate DnsQueryCandidate; typedef struct DnsQueryCandidate DnsQueryCandidate;
typedef struct DnsQuery DnsQuery; typedef struct DnsQuery DnsQuery;
@ -48,10 +49,6 @@ struct DnsQuery {
uint64_t flags; uint64_t flags;
int ifindex; 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 */ /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */
bool clamp_ttl; bool clamp_ttl;
@ -72,8 +69,9 @@ struct DnsQuery {
int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
bool previous_redirect_unauthenticated; bool previous_redirect_unauthenticated;
/* Bus client information */ /* Bus + Varlink client information */
sd_bus_message *request; sd_bus_message *bus_request;
Varlink *varlink_request;
int request_family; int request_family;
bool request_address_valid; bool request_address_valid;
union in_addr_union request_address; 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_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); DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol);

View File

@ -36,6 +36,7 @@
#include "resolved-manager.h" #include "resolved-manager.h"
#include "resolved-mdns.h" #include "resolved-mdns.h"
#include "resolved-resolv-conf.h" #include "resolved-resolv-conf.h"
#include "resolved-varlink.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
@ -663,6 +664,10 @@ int manager_start(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = manager_varlink_init(m);
if (r < 0)
return r;
return 0; return 0;
} }
@ -706,6 +711,7 @@ Manager *manager_free(Manager *m) {
manager_llmnr_stop(m); manager_llmnr_stop(m);
manager_mdns_stop(m); manager_mdns_stop(m);
manager_dns_stub_stop(m); manager_dns_stub_stop(m);
manager_varlink_done(m);
bus_verify_polkit_async_registry_free(m->polkit_registry); bus_verify_polkit_async_registry_free(m->polkit_registry);

View File

@ -11,6 +11,7 @@
#include "list.h" #include "list.h"
#include "ordered-set.h" #include "ordered-set.h"
#include "resolve-util.h" #include "resolve-util.h"
#include "varlink.h"
typedef struct Manager Manager; typedef struct Manager Manager;
@ -140,6 +141,8 @@ struct Manager {
sd_event_source *dns_stub_tcp_event_source; sd_event_source *dns_stub_tcp_event_source;
Hashmap *polkit_registry; Hashmap *polkit_registry;
VarlinkServer *varlink_server;
}; };
/* Manager */ /* 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; 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: case _JSON_BUILD_OBJECT_BEGIN:
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { 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_STRV,
_JSON_BUILD_BASE64, _JSON_BUILD_BASE64,
_JSON_BUILD_ID128, _JSON_BUILD_ID128,
_JSON_BUILD_BYTE_ARRAY,
_JSON_BUILD_MAX, _JSON_BUILD_MAX,
}; };
@ -249,6 +250,7 @@ enum {
#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; }) #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_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_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_build(JsonVariant **ret, ...);
int json_buildv(JsonVariant **ret, va_list ap); 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)); assert_cc(sizeof(intmax_t) == sizeof(int64_t));
#define json_dispatch_int64 json_dispatch_integer #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) { 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 /* 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. */ /* 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)) { 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) if (r < 0)
return r; return r;
} }
@ -1659,6 +1659,13 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) {
return -EINVAL; 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) { int varlink_notify(Varlink *v, JsonVariant *parameters) {
_cleanup_(json_variant_unrefp) JsonVariant *m = NULL; _cleanup_(json_variant_unrefp) JsonVariant *m = NULL;
int r; 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_error(Varlink *v, const char *error_id, JsonVariant *parameters);
int varlink_errorb(Varlink *v, const char *error_id, ...); int varlink_errorb(Varlink *v, const char *error_id, ...);
int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters); int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters);
int varlink_error_errno(Varlink *v, int error);
/* Enqueue a "more" reply */ /* Enqueue a "more" reply */
int varlink_notify(Varlink *v, JsonVariant *parameters); int varlink_notify(Varlink *v, JsonVariant *parameters);

View File

@ -1005,7 +1005,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom(); return log_oom();
/* If the user asked for a particular /* If the user asked for a particular
* property, show it to him, even if it is * property, show it to them, even if it is
* empty. */ * empty. */
arg_all = true; arg_all = true;
break; break;

View File

@ -186,7 +186,7 @@ static int dev_pci_onboard(sd_device *dev, struct netnames *names) {
/* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to
* report for example). Let's define a cut-off where we don't consider the index reliable anymore. We * report for example). Let's define a cut-off where we don't consider the index reliable anymore. We
* pick some arbitrary cut-off, which is somewhere beyond the realistic number of physical network * pick some arbitrary cut-off, which is somewhere beyond the realistic number of physical network
* interface a system might have. Ideally the kernel would already filter his crap for us, but it * interface a system might have. Ideally the kernel would already filter this crap for us, but it
* doesn't currently. */ * doesn't currently. */
if (idx > ONBOARD_INDEX_MAX) if (idx > ONBOARD_INDEX_MAX)
return -ENOENT; return -ENOENT;