Compare commits
17 Commits
957ef5874a
...
4988a38067
Author | SHA1 | Date |
---|---|---|
Nick Rosbrook | 4988a38067 | |
Daan De Meyer | dbbe895807 | |
Yu Watanabe | 52b0351a15 | |
Luca Boccassi | fe077a1a58 | |
Mike Yuan | b718b86e1b | |
Mike Yuan | d911778877 | |
Mike Yuan | eea9d3eb10 | |
Mike Yuan | 579ce77ead | |
Nick Rosbrook | c15ab51249 | |
Nick Rosbrook | e9d265bc73 | |
Nick Rosbrook | 739d63811e | |
Nick Rosbrook | 40cb08f8c1 | |
Nick Rosbrook | 0a87b4aee1 | |
Nick Rosbrook | 5b4af64599 | |
Nick Rosbrook | 95911ee908 | |
Nick Rosbrook | ad68334774 | |
Nick Rosbrook | 133314b44e |
|
@ -144,6 +144,22 @@
|
|||
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--dns</option></term>
|
||||
|
||||
<listitem><para>Waiting for DNS servers to be accessible on each interface. If this
|
||||
option is specified with <option>--any</option>, then
|
||||
<command>systemd-networkd-wait-online</command> exits with success when at least one interface
|
||||
becomes online and has an accessible DNS server.</para>
|
||||
<para>If a link has the property <varname>DefaultRoute=yes</varname> (either because the
|
||||
<varname>DNSDefaultRoute=</varname> network property is explicitly configured, or
|
||||
because the link does not have any "routing-only" domains), or if the search domain "." is
|
||||
configured, then wait for link-specific DNS servers to be accessible. Otherwise, allow global
|
||||
DNS servers to satisfy the condition.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--any</option></term>
|
||||
|
||||
|
|
|
@ -220,9 +220,9 @@ static int synthesize_user_creds(
|
|||
if (ret_gid)
|
||||
*ret_gid = GID_NOBODY;
|
||||
if (ret_home)
|
||||
*ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
|
||||
*ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
|
||||
if (ret_shell)
|
||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
|
||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ int get_user_creds(
|
|||
|
||||
assert(username);
|
||||
assert(*username);
|
||||
assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
|
||||
|
||||
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
||||
(!ret_home && !ret_shell)) {
|
||||
|
@ -315,17 +316,14 @@ int get_user_creds(
|
|||
|
||||
if (ret_home)
|
||||
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||
(empty_or_root(p->pw_dir) ||
|
||||
!path_is_valid(p->pw_dir) ||
|
||||
!path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
|
||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
|
||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
|
||||
? NULL : p->pw_dir;
|
||||
|
||||
if (ret_shell)
|
||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||
(isempty(p->pw_shell) ||
|
||||
!path_is_valid(p->pw_shell) ||
|
||||
!path_is_absolute(p->pw_shell) ||
|
||||
is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
|
||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
|
||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
|
||||
? NULL : p->pw_shell;
|
||||
|
||||
if (patch_username)
|
||||
*username = p->pw_name;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "string-util.h"
|
||||
|
||||
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
||||
#define HOME_UID_MIN ((uid_t) 60001)
|
||||
#define HOME_UID_MAX ((uid_t) 60513)
|
||||
|
@ -36,10 +38,20 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
|
|||
char* getlogname_malloc(void);
|
||||
char* getusername_malloc(void);
|
||||
|
||||
const char* default_root_shell_at(int rfd);
|
||||
const char* default_root_shell(const char *root);
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
|
||||
static inline bool shell_is_placeholder(const char *shell) {
|
||||
return isempty(shell) || is_nologin_shell(shell);
|
||||
}
|
||||
|
||||
typedef enum UserCredsFlags {
|
||||
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
||||
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
||||
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
||||
USER_CREDS_SUPPRESS_PLACEHOLDER = 1 << 3, /* suppress home and/or shell fields if value is placeholder (root/empty/nologin) */
|
||||
} UserCredsFlags;
|
||||
|
||||
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
||||
|
@ -125,10 +137,6 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg);
|
|||
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
||||
#endif
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
const char* default_root_shell_at(int rfd);
|
||||
const char* default_root_shell(const char *root);
|
||||
|
||||
int is_this_me(const char *username);
|
||||
|
||||
const char* get_home_root(void);
|
||||
|
|
|
@ -855,9 +855,6 @@ static int get_fixed_user(
|
|||
assert(user_or_uid);
|
||||
assert(ret_username);
|
||||
|
||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||
* (i.e. are "/" or "/bin/nologin"). */
|
||||
|
||||
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1883,7 +1880,10 @@ static int build_environment(
|
|||
}
|
||||
}
|
||||
|
||||
if (home && set_user_login_env) {
|
||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||
* (i.e. are "/" or "/bin/nologin"). */
|
||||
|
||||
if (home && set_user_login_env && !empty_or_root(home)) {
|
||||
x = strjoin("HOME=", home);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
@ -1892,7 +1892,7 @@ static int build_environment(
|
|||
our_env[n_env++] = x;
|
||||
}
|
||||
|
||||
if (shell && set_user_login_env) {
|
||||
if (shell && set_user_login_env && !shell_is_placeholder(shell)) {
|
||||
x = strjoin("SHELL=", shell);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
@ -3471,20 +3471,16 @@ static int apply_working_directory(
|
|||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
ExecRuntime *runtime,
|
||||
const char *home,
|
||||
int *exit_status) {
|
||||
const char *home) {
|
||||
|
||||
const char *wd;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(exit_status);
|
||||
|
||||
if (context->working_directory_home) {
|
||||
if (!home) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
if (!home)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
wd = home;
|
||||
} else
|
||||
|
@ -3503,13 +3499,7 @@ static int apply_working_directory(
|
|||
if (r >= 0)
|
||||
r = RET_NERRNO(fchdir(dfd));
|
||||
}
|
||||
|
||||
if (r < 0 && !context->working_directory_missing_ok) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return context->working_directory_missing_ok ? 0 : r;
|
||||
}
|
||||
|
||||
static int apply_root_directory(
|
||||
|
@ -3785,7 +3775,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf)
|
|||
if (!c->working_directory_home)
|
||||
return 0;
|
||||
|
||||
if (c->dynamic_user)
|
||||
if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
r = get_home_dir(ret_buf);
|
||||
|
@ -4543,7 +4533,7 @@ int exec_invoke(
|
|||
r = acquire_home(context, &home, &home_buffer);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for user: %m");
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking user: %m");
|
||||
}
|
||||
|
||||
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
||||
|
@ -5382,9 +5372,11 @@ int exec_invoke(
|
|||
* running this service might have the correct privilege to change to the working directory. Also, it
|
||||
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
||||
* the cwd cannot be used to pin directories outside of the sandbox. */
|
||||
r = apply_working_directory(context, params, runtime, home, exit_status);
|
||||
if (r < 0)
|
||||
r = apply_working_directory(context, params, runtime, home);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
||||
}
|
||||
|
||||
if (needs_sandboxing) {
|
||||
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
||||
|
|
|
@ -86,6 +86,19 @@ int varlink_callb_and_log(
|
|||
return varlink_call_and_log(v, method, parameters, ret_parameters);
|
||||
}
|
||||
|
||||
int varlink_many_notify(Set *s, sd_json_variant *parameters) {
|
||||
sd_varlink *link;
|
||||
int r = 1;
|
||||
|
||||
if (set_isempty(s))
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(link, s)
|
||||
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int varlink_many_notifyb(Set *s, ...) {
|
||||
int r;
|
||||
|
||||
|
@ -102,12 +115,7 @@ int varlink_many_notifyb(Set *s, ...) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = 1;
|
||||
sd_varlink *link;
|
||||
SET_FOREACH(link, s)
|
||||
RET_GATHER(r, sd_varlink_notify(link, parameters));
|
||||
|
||||
return r;
|
||||
return varlink_many_notify(s, parameters);
|
||||
}
|
||||
|
||||
int varlink_many_reply(Set *s, sd_json_variant *parameters) {
|
||||
|
|
|
@ -13,6 +13,7 @@ int varlink_callb_and_log(sd_varlink *v, const char *method, sd_json_variant **r
|
|||
#define varlink_callbo_and_log(v, method, ret_parameters, ...) \
|
||||
varlink_callb_and_log((v), (method), (ret_parameters), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
|
||||
int varlink_many_notify(Set *s, sd_json_variant *parameters);
|
||||
int varlink_many_notifyb(Set *s, ...);
|
||||
#define varlink_many_notifybo(s, ...) \
|
||||
varlink_many_notifyb((s), SD_JSON_BUILD_OBJECT(__VA_ARGS__))
|
||||
|
|
|
@ -114,6 +114,7 @@ sources = files(
|
|||
systemd_networkd_sources = files('networkd.c')
|
||||
|
||||
systemd_networkd_wait_online_sources = files(
|
||||
'wait-online/dns-configuration.c',
|
||||
'wait-online/link.c',
|
||||
'wait-online/manager.c',
|
||||
'wait-online/wait-online.c',
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "iovec-util.h"
|
||||
#include "hash-funcs.h"
|
||||
#include "json-util.h"
|
||||
#include "set.h"
|
||||
#include "strv.h"
|
||||
|
||||
DNSServer *dns_server_free(DNSServer *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
free(s->server_name);
|
||||
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
dns_server_hash_ops,
|
||||
void,
|
||||
trivial_hash_func,
|
||||
trivial_compare_func,
|
||||
DNSServer,
|
||||
dns_server_free);
|
||||
|
||||
static int dispatch_dns_server(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dns_server_dispatch_table[] = {
|
||||
{ "address", SD_JSON_VARIANT_ARRAY, NULL, 0, SD_JSON_MANDATORY },
|
||||
{ "family", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint, 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 },
|
||||
{ "accessible", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSServer, accessible), SD_JSON_MANDATORY },
|
||||
{},
|
||||
};
|
||||
DNSServer **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
int r;
|
||||
|
||||
s = new0(DNSServer, 1);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_dispatch(variant, dns_server_dispatch_table, flags, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* We ignored the address field above. Handle it now. */
|
||||
r = json_dispatch_byte_array_iovec("address", sd_json_variant_by_key(variant, "address"), flags, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (iov.iov_len != FAMILY_ADDRESS_SIZE_SAFE(s->family))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field 'address' is array of unexpected size.");
|
||||
|
||||
memcpy(&s->addr, iov.iov_base, iov.iov_len);
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_dns_server_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
Set **ret = ASSERT_PTR(userdata);
|
||||
Set *dns_servers;
|
||||
sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
dns_servers = set_new(&dns_server_hash_ops);
|
||||
if (!dns_servers)
|
||||
return log_oom();
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
|
||||
_cleanup_(dns_server_freep) DNSServer *s = NULL;
|
||||
|
||||
s = new0(DNSServer, 1);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
r = dispatch_dns_server(name, v, flags, &s);
|
||||
if (r < 0)
|
||||
return json_log(v, flags, r, "JSON array element is not a valid DNSServer.");
|
||||
|
||||
r = set_put(dns_servers, TAKE_PTR(s));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
set_free_and_replace(*ret, dns_servers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SearchDomain *search_domain_free(SearchDomain *d) {
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
free(d->name);
|
||||
|
||||
return mfree(d);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
search_domain_hash_ops,
|
||||
void,
|
||||
trivial_hash_func,
|
||||
trivial_compare_func,
|
||||
SearchDomain,
|
||||
search_domain_free);
|
||||
|
||||
static int dispatch_search_domain(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field search_domain_dispatch_table[] = {
|
||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(SearchDomain, name), SD_JSON_MANDATORY },
|
||||
{ "routeOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(SearchDomain, route_only), SD_JSON_MANDATORY },
|
||||
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(SearchDomain, ifindex), SD_JSON_RELAX },
|
||||
{},
|
||||
};
|
||||
SearchDomain **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_(search_domain_freep) SearchDomain *d = NULL;
|
||||
int r;
|
||||
|
||||
d = new0(SearchDomain, 1);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_dispatch(variant, search_domain_dispatch_table, flags, d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispatch_search_domain_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
Set **ret = ASSERT_PTR(userdata);
|
||||
Set *search_domains;
|
||||
sd_json_variant *v = NULL;
|
||||
int r;
|
||||
|
||||
search_domains = set_new(&search_domain_hash_ops);
|
||||
if (!search_domains)
|
||||
return log_oom();
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(v, variant) {
|
||||
_cleanup_(search_domain_freep) SearchDomain *d = NULL;
|
||||
|
||||
d = new0(SearchDomain, 1);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = dispatch_search_domain(name, v, flags, &d);
|
||||
if (r < 0)
|
||||
return json_log(v, flags, r, "JSON array element is not a valid SearchDomain.");
|
||||
|
||||
r = set_put(search_domains, TAKE_PTR(d));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
set_free_and_replace(*ret, search_domains);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DNSConfiguration *dns_configuration_free(DNSConfiguration *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
dns_server_free(c->current_dns_server);
|
||||
set_free(c->dns_servers);
|
||||
set_free(c->search_domains);
|
||||
free(c->ifname);
|
||||
|
||||
return mfree(c);
|
||||
}
|
||||
|
||||
static int dispatch_dns_configuration(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dns_configuration_dispatch_table[] = {
|
||||
{ "ifname", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DNSConfiguration, ifname), 0 },
|
||||
{ "ifindex", SD_JSON_VARIANT_UNSIGNED, json_dispatch_ifindex, offsetof(DNSConfiguration, ifindex), SD_JSON_RELAX },
|
||||
{ "defaultRoute", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DNSConfiguration, default_route), 0 },
|
||||
{ "currentServer", SD_JSON_VARIANT_OBJECT, dispatch_dns_server, offsetof(DNSConfiguration, current_dns_server), 0 },
|
||||
{ "servers", SD_JSON_VARIANT_ARRAY, dispatch_dns_server_array, offsetof(DNSConfiguration, dns_servers), 0 },
|
||||
{ "searchDomains", SD_JSON_VARIANT_ARRAY, dispatch_search_domain_array, offsetof(DNSConfiguration, search_domains), 0 },
|
||||
{},
|
||||
|
||||
};
|
||||
DNSConfiguration **ret = ASSERT_PTR(userdata);
|
||||
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||
int r;
|
||||
|
||||
c = new0(DNSConfiguration, 1);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
r = sd_json_dispatch(variant, dns_configuration_dispatch_table, flags, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret) {
|
||||
return dispatch_dns_configuration(NULL, variant, SD_JSON_LOG, ret);
|
||||
}
|
||||
|
||||
bool dns_is_accessible(DNSConfiguration *c) {
|
||||
DNSServer *s = NULL;
|
||||
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
if (c->current_dns_server && c->current_dns_server->accessible)
|
||||
return true;
|
||||
|
||||
SET_FOREACH(s, c->dns_servers)
|
||||
if (s->accessible)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain) {
|
||||
SearchDomain *d = NULL;
|
||||
|
||||
assert(domain);
|
||||
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
SET_FOREACH(d, c->search_domains)
|
||||
if (streq(d->name, domain))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "macro-fundamental.h"
|
||||
#include "set.h"
|
||||
|
||||
typedef struct DNSServer DNSServer;
|
||||
typedef struct SearchDomain SearchDomain;
|
||||
typedef struct DNSConfiguration DNSConfiguration;
|
||||
|
||||
struct DNSServer {
|
||||
union in_addr_union addr;
|
||||
int family;
|
||||
uint16_t port;
|
||||
int ifindex;
|
||||
char *server_name;
|
||||
bool accessible;
|
||||
};
|
||||
|
||||
DNSServer *dns_server_free(DNSServer *s);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSServer*, dns_server_free);
|
||||
|
||||
struct SearchDomain {
|
||||
char *name;
|
||||
bool route_only;
|
||||
int ifindex;
|
||||
};
|
||||
|
||||
SearchDomain *search_domain_free(SearchDomain *d);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(SearchDomain*, search_domain_free);
|
||||
|
||||
struct DNSConfiguration {
|
||||
char *ifname;
|
||||
int ifindex;
|
||||
bool default_route;
|
||||
DNSServer *current_dns_server;
|
||||
Set *dns_servers;
|
||||
Set *search_domains;
|
||||
};
|
||||
|
||||
int dns_configuration_from_json(sd_json_variant *variant, DNSConfiguration **ret);
|
||||
bool dns_is_accessible(DNSConfiguration *c);
|
||||
bool dns_configuration_contains_search_domain(DNSConfiguration *c, const char *domain);
|
||||
|
||||
DNSConfiguration *dns_configuration_free(DNSConfiguration *c);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DNSConfiguration*, dns_configuration_free);
|
|
@ -3,6 +3,7 @@
|
|||
#include "sd-network.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "format-ifname.h"
|
||||
#include "hashmap.h"
|
||||
#include "link.h"
|
||||
|
@ -62,6 +63,8 @@ Link *link_free(Link *l) {
|
|||
hashmap_remove(l->manager->links_by_name, *n);
|
||||
}
|
||||
|
||||
dns_configuration_free(l->dns_configuration);
|
||||
|
||||
free(l->state);
|
||||
free(l->ifname);
|
||||
strv_free(l->altnames);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "dns-configuration.h"
|
||||
#include "log-link.h"
|
||||
#include "network-util.h"
|
||||
|
||||
|
@ -24,6 +25,7 @@ struct Link {
|
|||
LinkAddressState ipv4_address_state;
|
||||
LinkAddressState ipv6_address_state;
|
||||
char *state;
|
||||
DNSConfiguration *dns_configuration;
|
||||
};
|
||||
|
||||
int link_new(Manager *m, Link **ret, int ifindex, const char *ifname);
|
||||
|
|
|
@ -4,7 +4,13 @@
|
|||
#include <linux/if.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-configuration.h"
|
||||
#include "json-util.h"
|
||||
#include "link.h"
|
||||
#include "manager.h"
|
||||
#include "netlink-util.h"
|
||||
|
@ -133,6 +139,26 @@ static int manager_link_is_online(Manager *m, Link *l, const LinkOperationalStat
|
|||
"No routable IPv6 address is configured.");
|
||||
}
|
||||
|
||||
if (m->requires_dns) {
|
||||
if (!l->dns_configuration)
|
||||
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||
"No DNS configuration yet");
|
||||
|
||||
/* If a link is configured with DNSDefaultRoute=yes, or is configured with the
|
||||
* search domain '.', then require link-specific DNS servers to be available.
|
||||
* Otherwise, we check the global DNS configuration. */
|
||||
if (l->dns_configuration->default_route ||
|
||||
dns_configuration_contains_search_domain(l->dns_configuration, ".")) {
|
||||
|
||||
if (!dns_is_accessible(l->dns_configuration))
|
||||
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||
"No link-specific DNS server is accessible.");
|
||||
|
||||
} else if (!dns_is_accessible(m->dns_configuration))
|
||||
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EADDRNOTAVAIL),
|
||||
"No DNS server is accessible.");
|
||||
}
|
||||
|
||||
log_link_debug(l, "link is configured by networkd and online.");
|
||||
return true;
|
||||
}
|
||||
|
@ -381,13 +407,102 @@ static int manager_network_monitor_listen(Manager *m) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int on_dns_configuration_event(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
const char *error_id,
|
||||
sd_varlink_reply_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_json_variant *configurations = NULL, *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (error_id) {
|
||||
log_warning("DNS configuration event error, ignoring: %s", error_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
configurations = sd_json_variant_by_key(parameters, "configuration");
|
||||
if (!sd_json_variant_is_array(configurations)) {
|
||||
log_warning("DNS configuration JSON data does not have configuration key, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(v, configurations) {
|
||||
_cleanup_(dns_configuration_freep) DNSConfiguration *c = NULL;
|
||||
|
||||
r = dns_configuration_from_json(v, &c);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to get DNS configuration JSON, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c->ifindex > 0) {
|
||||
Link *l = hashmap_get(m->links_by_index, INT_TO_PTR(c->ifindex));
|
||||
if (l)
|
||||
free_and_replace_full(l->dns_configuration, c, dns_configuration_free);
|
||||
} else
|
||||
/* Global DNS configuration */
|
||||
free_and_replace_full(m->dns_configuration, c, dns_configuration_free);
|
||||
}
|
||||
|
||||
if (manager_configured(m))
|
||||
sd_event_exit(m->event, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_dns_configuration_listen(Manager *m) {
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(m->event);
|
||||
|
||||
if (!m->requires_dns)
|
||||
return 0;
|
||||
|
||||
r = sd_varlink_connect_address(&vl, "/run/systemd/resolve/io.systemd.Resolve.Monitor");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to io.systemd.Resolve.Monitor: %m");
|
||||
|
||||
r = sd_varlink_set_relative_timeout(vl, USEC_INFINITY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set varlink timeout: %m");
|
||||
|
||||
r = sd_varlink_attach_event(vl, m->event, SD_EVENT_PRIORITY_NORMAL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||
|
||||
(void) sd_varlink_set_userdata(vl, m);
|
||||
|
||||
r = sd_varlink_bind_reply(vl, on_dns_configuration_event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind varlink reply callback: %m");
|
||||
|
||||
r = sd_varlink_observebo(
|
||||
vl,
|
||||
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration",
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", false));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to issue SubscribeDNSConfiguration: %m");
|
||||
|
||||
m->varlink_client = TAKE_PTR(vl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_new(Manager **ret,
|
||||
Hashmap *command_line_interfaces_by_name,
|
||||
char **ignored_interfaces,
|
||||
LinkOperationalStateRange required_operstate,
|
||||
AddressFamily required_family,
|
||||
bool any,
|
||||
usec_t timeout) {
|
||||
usec_t timeout,
|
||||
bool requires_dns) {
|
||||
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
int r;
|
||||
|
@ -404,6 +519,7 @@ int manager_new(Manager **ret,
|
|||
.required_operstate = required_operstate,
|
||||
.required_family = required_family,
|
||||
.any = any,
|
||||
.requires_dns = requires_dns,
|
||||
};
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
|
@ -428,6 +544,10 @@ int manager_new(Manager **ret,
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_dns_configuration_listen(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
|
||||
return 0;
|
||||
|
@ -445,6 +565,9 @@ Manager* manager_free(Manager *m) {
|
|||
sd_event_source_unref(m->rtnl_event_source);
|
||||
sd_netlink_unref(m->rtnl);
|
||||
sd_event_unref(m->event);
|
||||
sd_varlink_unref(m->varlink_client);
|
||||
|
||||
dns_configuration_free(m->dns_configuration);
|
||||
|
||||
return mfree(m);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#include "sd-event.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-network.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "dns-configuration.h"
|
||||
#include "hashmap.h"
|
||||
#include "network-util.h"
|
||||
#include "time-util.h"
|
||||
|
@ -23,6 +25,7 @@ struct Manager {
|
|||
LinkOperationalStateRange required_operstate;
|
||||
AddressFamily required_family;
|
||||
bool any;
|
||||
bool requires_dns;
|
||||
|
||||
sd_netlink *rtnl;
|
||||
sd_event_source *rtnl_event_source;
|
||||
|
@ -31,13 +34,16 @@ struct Manager {
|
|||
sd_event_source *network_monitor_event_source;
|
||||
|
||||
sd_event *event;
|
||||
|
||||
sd_varlink *varlink_client;
|
||||
DNSConfiguration *dns_configuration;
|
||||
};
|
||||
|
||||
Manager* manager_free(Manager *m);
|
||||
int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
|
||||
LinkOperationalStateRange required_operstate,
|
||||
AddressFamily required_family,
|
||||
bool any, usec_t timeout);
|
||||
bool any, usec_t timeout, bool requires_dns);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "daemon-util.h"
|
||||
#include "main-func.h"
|
||||
#include "manager.h"
|
||||
#include "parse-argument.h"
|
||||
#include "pretty-print.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
|
@ -22,6 +23,7 @@ static char **arg_ignore = NULL;
|
|||
static LinkOperationalStateRange arg_required_operstate = LINK_OPERSTATE_RANGE_INVALID;
|
||||
static AddressFamily arg_required_family = ADDRESS_FAMILY_NO;
|
||||
static bool arg_any = false;
|
||||
static bool arg_requires_dns = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
|
||||
|
@ -48,6 +50,7 @@ static int help(void) {
|
|||
" -6 --ipv6 Requires at least one IPv6 address\n"
|
||||
" --any Wait until at least one of the interfaces is online\n"
|
||||
" --timeout=SECS Maximum time to wait for network connectivity\n"
|
||||
" --dns Requires at least one DNS server to be accessible\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
|
@ -106,6 +109,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_IGNORE,
|
||||
ARG_ANY,
|
||||
ARG_TIMEOUT,
|
||||
ARG_DNS,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -119,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "ipv6", no_argument, NULL, '6' },
|
||||
{ "any", no_argument, NULL, ARG_ANY },
|
||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||
{ "dns", optional_argument, NULL, ARG_DNS },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -178,6 +183,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return r;
|
||||
break;
|
||||
|
||||
case ARG_DNS:
|
||||
r = parse_boolean_argument("--dns", optarg, &arg_requires_dns);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -204,7 +215,14 @@ static int run(int argc, char *argv[]) {
|
|||
if (arg_quiet)
|
||||
log_set_max_level(LOG_ERR);
|
||||
|
||||
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
|
||||
r = manager_new(&m,
|
||||
arg_interfaces,
|
||||
arg_ignore,
|
||||
arg_required_operstate,
|
||||
arg_required_family,
|
||||
arg_any,
|
||||
arg_timeout,
|
||||
arg_requires_dns);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not create manager: %m");
|
||||
|
||||
|
|
|
@ -150,6 +150,17 @@
|
|||
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.resolve1.subscribe-dns-configuration">
|
||||
<description gettext-domain="systemd">Subscribe to DNS configuration</description>
|
||||
<message gettext-domain="systemd">Authentication is required to subscribe to DNS configuration.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-resolve</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.resolve1.dump-cache">
|
||||
<description gettext-domain="systemd">Dump cache</description>
|
||||
<message gettext-domain="systemd">Authentication is required to dump cache.</message>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dns-domain.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
|
@ -197,3 +199,21 @@ int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDo
|
|||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret) {
|
||||
int ifindex = 0;
|
||||
|
||||
assert(domain);
|
||||
assert(ret);
|
||||
|
||||
if (domain->type == DNS_SEARCH_DOMAIN_LINK) {
|
||||
assert(domain->link);
|
||||
ifindex = domain->link->ifindex;
|
||||
}
|
||||
|
||||
return sd_json_buildo(
|
||||
ret,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", domain->name),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("routeOnly", domain->route_only),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
|
||||
|
@ -54,3 +56,5 @@ static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) {
|
|||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
|
||||
|
||||
int dns_search_domain_dump_to_json(DnsSearchDomain *domain, sd_json_variant **ret);
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "resolved-bus.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-dns-stub.h"
|
||||
#include "resolved-manager.h"
|
||||
#include "resolved-resolv-conf.h"
|
||||
#include "siphash24.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
|
||||
|
@ -69,6 +75,7 @@ int dns_server_new(
|
|||
.ifindex = ifindex,
|
||||
.server_name = TAKE_PTR(name),
|
||||
.config_source = config_source,
|
||||
.accessible = -1,
|
||||
};
|
||||
|
||||
dns_server_reset_features(s);
|
||||
|
@ -875,6 +882,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
|
|||
dns_cache_flush(&m->unicast_scope->cache);
|
||||
|
||||
(void) manager_send_changed(m, "CurrentDNSServer");
|
||||
(void) manager_send_dns_configuration_changed(m);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -1128,3 +1136,58 @@ int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret) {
|
|||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketInvalid", server->packet_invalid),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("PacketDoOff", server->packet_do_off));
|
||||
}
|
||||
|
||||
int dns_server_is_accessible(DnsServer *s) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
union sockaddr_union sa;
|
||||
int ifindex, r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->accessible >= 0)
|
||||
return s->accessible;
|
||||
|
||||
r = sockaddr_set_in_addr(&sa, s->family, &s->address, dns_server_port(s));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = socket(s->family, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
ifindex = dns_server_ifindex(s);
|
||||
if (ifindex > 0) {
|
||||
r = socket_bind_to_ifindex(fd, ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = RET_NERRNO(connect(fd, &sa.sa, SOCKADDR_LEN(sa)));
|
||||
|
||||
return (s->accessible = r >= 0);
|
||||
}
|
||||
|
||||
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret) {
|
||||
bool accessible = false;
|
||||
int ifindex, r;
|
||||
|
||||
assert(server);
|
||||
assert(ret);
|
||||
|
||||
ifindex = dns_server_ifindex(server);
|
||||
|
||||
r = dns_server_is_accessible(server);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check if %s is accessible, assume not: %m", dns_server_string_full(server));
|
||||
else
|
||||
accessible = r;
|
||||
|
||||
return sd_json_buildo(
|
||||
ret,
|
||||
JSON_BUILD_PAIR_IN_ADDR("address", &server->address, server->family),
|
||||
SD_JSON_BUILD_PAIR_INTEGER("family", server->family),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("port", dns_server_port(server)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("name", server->server_name),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("accessible", accessible));
|
||||
}
|
||||
|
|
|
@ -106,6 +106,9 @@ struct DnsServer {
|
|||
|
||||
/* Servers registered via D-Bus are not removed on reload */
|
||||
ResolveConfigSource config_source;
|
||||
|
||||
/* Tri-state to indicate if the DNS server is accessible. */
|
||||
int accessible;
|
||||
};
|
||||
|
||||
int dns_server_new(
|
||||
|
@ -187,3 +190,9 @@ static inline bool dns_server_is_fallback(DnsServer *s) {
|
|||
}
|
||||
|
||||
int dns_server_dump_state_to_json(DnsServer *server, sd_json_variant **ret);
|
||||
int dns_server_dump_configuration_to_json(DnsServer *server, sd_json_variant **ret);
|
||||
|
||||
int dns_server_is_accessible(DnsServer *s);
|
||||
static inline void dns_server_reset_accessible(DnsServer *s) {
|
||||
s->accessible = -1;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dn
|
|||
static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_llmnr_support, "s", Link, link_get_llmnr_support, resolve_support_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_mdns_support, "s", Link, link_get_mdns_support, resolve_support_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_default_route, "b", Link, link_get_default_route);
|
||||
|
||||
static int property_get_dns_over_tls_mode(
|
||||
sd_bus *bus,
|
||||
|
@ -160,30 +161,6 @@ static int property_get_domains(
|
|||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_default_route(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Link *l = ASSERT_PTR(userdata);
|
||||
|
||||
assert(reply);
|
||||
|
||||
/* Return what is configured, if there's something configured */
|
||||
if (l->default_route >= 0)
|
||||
return sd_bus_message_append(reply, "b", l->default_route);
|
||||
|
||||
/* Otherwise report what is in effect */
|
||||
if (l->unicast_scope)
|
||||
return sd_bus_message_append(reply, "b", dns_scope_is_default_route(l->unicast_scope));
|
||||
|
||||
return sd_bus_message_append(reply, "b", false);
|
||||
}
|
||||
|
||||
static int property_get_scopes_mask(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
|
@ -293,6 +270,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
|
|||
(void) link_save_user(l);
|
||||
(void) manager_write_resolv_conf(l->manager);
|
||||
(void) manager_send_changed(l->manager, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(l->manager);
|
||||
|
||||
if (j)
|
||||
log_link_info(l, "Bus client set DNS server list to: %s", j);
|
||||
|
@ -768,6 +746,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
|
|||
(void) link_save_user(l);
|
||||
(void) manager_write_resolv_conf(l->manager);
|
||||
(void) manager_send_changed(l->manager, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(l->manager);
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mkdir.h"
|
||||
#include "netif-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "resolved-dns-scope.h"
|
||||
#include "resolved-link.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-mdns.h"
|
||||
|
@ -299,6 +300,12 @@ static int link_update_dns_servers(Link *l) {
|
|||
}
|
||||
|
||||
dns_server_unlink_marked(l->dns_servers);
|
||||
|
||||
LIST_FOREACH(servers, s, l->dns_servers)
|
||||
/* If the link state changed, we should re-check if DNS servers
|
||||
* are accessible. */
|
||||
dns_server_reset_accessible(s);
|
||||
|
||||
return 0;
|
||||
|
||||
clear:
|
||||
|
@ -831,6 +838,20 @@ ResolveSupport link_get_mdns_support(Link *link) {
|
|||
return MIN(link->mdns_support, link->manager->mdns_support);
|
||||
}
|
||||
|
||||
bool link_get_default_route(Link *l) {
|
||||
assert(l);
|
||||
|
||||
/* Return what is configured, if there's something configured */
|
||||
if (l->default_route >= 0)
|
||||
return l->default_route;
|
||||
|
||||
/* Otherwise report what is in effect */
|
||||
if (l->unicast_scope)
|
||||
return dns_scope_is_default_route(l->unicast_scope);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int link_address_new(Link *l,
|
||||
LinkAddress **ret,
|
||||
int family,
|
||||
|
|
|
@ -109,6 +109,8 @@ DnsOverTlsMode link_get_dns_over_tls_mode(Link *l);
|
|||
ResolveSupport link_get_llmnr_support(Link *link);
|
||||
ResolveSupport link_get_mdns_support(Link *link);
|
||||
|
||||
bool link_get_default_route(Link *l);
|
||||
|
||||
int link_save_user(Link *l);
|
||||
int link_load_user(Link *l);
|
||||
void link_remove_user(Link *l);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "idn-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "memstream-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "missing_socket.h"
|
||||
|
@ -286,6 +287,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
|
|||
|
||||
(void) manager_write_resolv_conf(m);
|
||||
(void) manager_send_changed(m, "DNS");
|
||||
(void) manager_send_dns_configuration_changed(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1184,7 +1186,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
|||
|
||||
assert(m);
|
||||
|
||||
if (set_isempty(m->varlink_subscription))
|
||||
if (set_isempty(m->varlink_query_results_subscription))
|
||||
return 0;
|
||||
|
||||
/* Merge all questions into one */
|
||||
|
@ -1234,7 +1236,7 @@ int manager_monitor_send(Manager *m, DnsQuery *q) {
|
|||
}
|
||||
|
||||
r = varlink_many_notifybo(
|
||||
m->varlink_subscription,
|
||||
m->varlink_query_results_subscription,
|
||||
SD_JSON_BUILD_PAIR("state", SD_JSON_BUILD_STRING(dns_transaction_state_to_string(q->state))),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(q->state == DNS_TRANSACTION_DNSSEC_FAILED,
|
||||
"result", SD_JSON_BUILD_STRING(dnssec_result_to_string(q->answer_dnssec_result))),
|
||||
|
@ -1932,3 +1934,124 @@ void dns_manager_reset_statistics(Manager *m) {
|
|||
m->n_failure_responses_served_stale_total = 0;
|
||||
zero(m->n_dnssec_verdict);
|
||||
}
|
||||
|
||||
static int dns_configuration_json_append(
|
||||
const char *ifname,
|
||||
int ifindex,
|
||||
int default_route,
|
||||
DnsServer *current_dns_server,
|
||||
DnsServer *dns_servers,
|
||||
DnsSearchDomain *search_domains,
|
||||
sd_json_variant **configuration) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *dns_servers_json = NULL,
|
||||
*search_domains_json = NULL,
|
||||
*current_dns_server_json = NULL;
|
||||
int r;
|
||||
|
||||
assert(configuration);
|
||||
|
||||
r = sd_json_variant_new_array(&dns_servers_json, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_new_array(&search_domains_json, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (current_dns_server) {
|
||||
r = dns_server_dump_configuration_to_json(current_dns_server, ¤t_dns_server_json);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(servers, s, dns_servers) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
|
||||
r = dns_server_dump_configuration_to_json(s, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&dns_servers_json, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(domains, d, search_domains) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
|
||||
r = dns_search_domain_dump_to_json(d, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_append_array(&search_domains_json, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_variant_append_arraybo(
|
||||
configuration,
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("ifname", ifname),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", SD_JSON_BUILD_UNSIGNED(ifindex)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(ifindex > 0, "defaultRoute", SD_JSON_BUILD_BOOLEAN(default_route > 0)),
|
||||
JSON_BUILD_PAIR_VARIANT_NON_NULL("currentServer", current_dns_server_json),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("servers", dns_servers_json),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("searchDomains", search_domains_json));
|
||||
}
|
||||
|
||||
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
Link *l;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
/* Global DNS configuration */
|
||||
r = dns_configuration_json_append(
|
||||
/* ifname = */ NULL,
|
||||
/* ifindex = */ 0,
|
||||
/* default_route = */ 0,
|
||||
manager_get_dns_server(m),
|
||||
m->dns_servers,
|
||||
m->search_domains,
|
||||
&configuration);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Append configuration for each link */
|
||||
HASHMAP_FOREACH(l, m->links) {
|
||||
r = dns_configuration_json_append(
|
||||
l->ifname,
|
||||
l->ifindex,
|
||||
link_get_default_route(l),
|
||||
link_get_dns_server(l),
|
||||
l->dns_servers,
|
||||
l->search_domains,
|
||||
&configuration);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_json_buildo(ret, SD_JSON_BUILD_PAIR_VARIANT("configuration", configuration));
|
||||
}
|
||||
|
||||
int manager_send_dns_configuration_changed(Manager *m) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (set_isempty(m->varlink_dns_configuration_subscription))
|
||||
return 0;
|
||||
|
||||
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to dump DNS configuration json: %m");
|
||||
|
||||
r = varlink_many_notify(m->varlink_dns_configuration_subscription, configuration);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to send DNS configuration event: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -152,7 +152,8 @@ struct Manager {
|
|||
sd_varlink_server *varlink_server;
|
||||
sd_varlink_server *varlink_monitor_server;
|
||||
|
||||
Set *varlink_subscription;
|
||||
Set *varlink_query_results_subscription;
|
||||
Set *varlink_dns_configuration_subscription;
|
||||
|
||||
sd_event_source *clock_change_event_source;
|
||||
|
||||
|
@ -225,3 +226,6 @@ int socket_disable_pmtud(int fd, int af);
|
|||
int dns_manager_dump_statistics_json(Manager *m, sd_json_variant **ret);
|
||||
|
||||
void dns_manager_reset_statistics(Manager *m);
|
||||
|
||||
int manager_dump_dns_configuration_json(Manager *m, sd_json_variant **ret);
|
||||
int manager_send_dns_configuration_changed(Manager *m);
|
||||
|
|
|
@ -125,14 +125,21 @@ static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userd
|
|||
|
||||
static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_varlink *removed_link = NULL;
|
||||
|
||||
assert(s);
|
||||
assert(link);
|
||||
|
||||
sd_varlink *removed_link = set_remove(m->varlink_subscription, link);
|
||||
removed_link = set_remove(m->varlink_query_results_subscription, link);
|
||||
if (removed_link) {
|
||||
sd_varlink_unref(removed_link);
|
||||
log_debug("%u monitor clients remain active", set_size(m->varlink_subscription));
|
||||
log_debug("%u query result monitor clients remain active", set_size(m->varlink_query_results_subscription));
|
||||
}
|
||||
|
||||
removed_link = set_remove(m->varlink_dns_configuration_subscription, link);
|
||||
if (removed_link) {
|
||||
sd_varlink_unref(removed_link);
|
||||
log_debug("%u DNS monitor clients remain active", set_size(m->varlink_dns_configuration_subscription));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1227,12 +1234,12 @@ static int vl_method_subscribe_query_results(sd_varlink *link, sd_json_variant *
|
|||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to report monitor to be established: %m");
|
||||
|
||||
r = set_ensure_put(&m->varlink_subscription, NULL, link);
|
||||
r = set_ensure_put(&m->varlink_query_results_subscription, NULL, link);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||
sd_varlink_ref(link);
|
||||
|
||||
log_debug("%u clients now attached for varlink notifications", set_size(m->varlink_subscription));
|
||||
log_debug("%u clients now attached for query result varlink notifications", set_size(m->varlink_query_results_subscription));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1352,6 +1359,38 @@ static int vl_method_reset_statistics(sd_varlink *link, sd_json_variant *paramet
|
|||
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
|
||||
}
|
||||
|
||||
static int vl_method_subscribe_dns_configuration(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *configuration = NULL;
|
||||
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link)));
|
||||
int r;
|
||||
|
||||
/* if the client didn't set the more flag, it is using us incorrectly */
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
r = verify_polkit(link, parameters, "org.freedesktop.resolve1.subscribe-dns-configuration");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = manager_dump_dns_configuration_json(m, &configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dump current DNS configuration: %m");
|
||||
|
||||
r = sd_varlink_notify(link, configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send current DNS configuration: %m");
|
||||
|
||||
r = set_ensure_put(&m->varlink_dns_configuration_subscription, NULL, link);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add subscription to set: %m");
|
||||
sd_varlink_ref(link);
|
||||
|
||||
log_debug("%u clients now attached for link configuration varlink notifications",
|
||||
set_size(m->varlink_dns_configuration_subscription));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int varlink_monitor_server_init(Manager *m) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
|
||||
int r;
|
||||
|
@ -1377,7 +1416,8 @@ static int varlink_monitor_server_init(Manager *m) {
|
|||
"io.systemd.Resolve.Monitor.DumpCache", vl_method_dump_cache,
|
||||
"io.systemd.Resolve.Monitor.DumpServerState", vl_method_dump_server_state,
|
||||
"io.systemd.Resolve.Monitor.DumpStatistics", vl_method_dump_statistics,
|
||||
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics);
|
||||
"io.systemd.Resolve.Monitor.ResetStatistics", vl_method_reset_statistics,
|
||||
"io.systemd.Resolve.Monitor.SubscribeDNSConfiguration", vl_method_subscribe_dns_configuration);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||
|
||||
|
|
|
@ -2297,7 +2297,8 @@ static int start_transient_scope(sd_bus *bus) {
|
|||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS);
|
||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
|
||||
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
||||
|
||||
|
|
|
@ -112,6 +112,52 @@ static SD_VARLINK_DEFINE_METHOD(
|
|||
ResetStatistics,
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNSServer,
|
||||
SD_VARLINK_FIELD_COMMENT("IPv4 or IPv6 address of the server."),
|
||||
SD_VARLINK_DEFINE_FIELD(address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("Address family of the server, one of AF_INET or AF_INET6."),
|
||||
SD_VARLINK_DEFINE_FIELD(family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Port number of the server."),
|
||||
SD_VARLINK_DEFINE_FIELD(port, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Interface index for which this server is configured."),
|
||||
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Server Name Indication (SNI) of the server."),
|
||||
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates if the DNS server is accessible or not."),
|
||||
SD_VARLINK_DEFINE_FIELD(accessible, SD_VARLINK_BOOL, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SearchDomain,
|
||||
SD_VARLINK_FIELD_COMMENT("Domain name."),
|
||||
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates whether or not this is a routing-only domain."),
|
||||
SD_VARLINK_DEFINE_FIELD(routeOnly, SD_VARLINK_BOOL, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Interface index for which this search domain is configured."),
|
||||
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
DNSConfiguration,
|
||||
SD_VARLINK_FIELD_COMMENT("Interface name, if any, associated with this configuration. Empty for global configuration."),
|
||||
SD_VARLINK_DEFINE_FIELD(ifname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Interface index, if any, associated with this configuration. Empty for global configuration."),
|
||||
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates whether or not this link's DNS servers will be used for resolving domain names that do not match any link's configured domains."),
|
||||
SD_VARLINK_DEFINE_FIELD(defaultRoute, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("DNS server currently selected to use for lookups."),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(currentServer, DNSServer, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Array of configured DNS servers."),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(servers, DNSServer, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Array of configured search domains."),
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(searchDomains, SearchDomain, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
SubscribeDNSConfiguration,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The current global and per-interface DNS configurations"),
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(configuration, DNSConfiguration, SD_VARLINK_ARRAY));
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Resolve_Monitor,
|
||||
"io.systemd.Resolve.Monitor",
|
||||
|
@ -129,4 +175,12 @@ SD_VARLINK_DEFINE_INTERFACE(
|
|||
&vl_type_TransactionStatistics,
|
||||
&vl_type_CacheStatistics,
|
||||
&vl_type_DnssecStatistics,
|
||||
&vl_type_ServerState);
|
||||
&vl_type_ServerState,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a DNS server address specification."),
|
||||
&vl_type_DNSServer,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a search domain specification."),
|
||||
&vl_type_SearchDomain,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a global or per-link DNS configuration, including configured DNS servers, search domains, and more."),
|
||||
&vl_type_DNSConfiguration,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Sends the complete global and per-link DNS configurations when any changes are made to them. The current configurations are given immediately when this method is invoked."),
|
||||
&vl_method_SubscribeDNSConfiguration);
|
||||
|
|
|
@ -7,24 +7,26 @@ TEST(audit_loginuid_from_pid) {
|
|||
_cleanup_(pidref_done) PidRef self = PIDREF_NULL, pid1 = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
assert_se(pidref_set_self(&self) >= 0);
|
||||
assert_se(pidref_set_pid(&pid1, 1) >= 0);
|
||||
ASSERT_OK(pidref_set_self(&self));
|
||||
ASSERT_OK(pidref_set_pid(&pid1, 1));
|
||||
|
||||
uid_t uid;
|
||||
r = audit_loginuid_from_pid(&self, &uid);
|
||||
assert_se(r >= 0 || r == -ENODATA);
|
||||
if (r != -ENODATA)
|
||||
ASSERT_OK(r);
|
||||
if (r >= 0)
|
||||
log_info("self audit login uid: " UID_FMT, uid);
|
||||
|
||||
assert_se(audit_loginuid_from_pid(&pid1, &uid) == -ENODATA);
|
||||
ASSERT_ERROR(audit_loginuid_from_pid(&pid1, &uid), ENODATA);
|
||||
|
||||
uint32_t sessionid;
|
||||
r = audit_session_from_pid(&self, &sessionid);
|
||||
assert_se(r >= 0 || r == -ENODATA);
|
||||
if (r != -ENODATA)
|
||||
ASSERT_OK(r);
|
||||
if (r >= 0)
|
||||
log_info("self audit session id: %" PRIu32, sessionid);
|
||||
|
||||
assert_se(audit_session_from_pid(&pid1, &sessionid) == -ENODATA);
|
||||
ASSERT_ERROR(audit_session_from_pid(&pid1, &sessionid), ENODATA);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
|
|
|
@ -1086,6 +1086,139 @@ DNS=127.0.0.1
|
|||
self.show_journal('systemd-timedated.service')
|
||||
self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu')
|
||||
|
||||
def do_test_wait_online_dns(
|
||||
self,
|
||||
iface,
|
||||
global_dns='',
|
||||
fallback_dns='',
|
||||
expect_timeout=False
|
||||
):
|
||||
if expect_timeout:
|
||||
timeout = 5
|
||||
else:
|
||||
timeout = 10
|
||||
|
||||
# Unless given, clear global DNS configuration
|
||||
conf = '/run/systemd/resolved.conf.d/global-dns.conf'
|
||||
os.makedirs(os.path.dirname(conf), exist_ok=True)
|
||||
with open(conf, 'w') as f:
|
||||
f.write((
|
||||
'[Resolve]\n'
|
||||
f'DNS={global_dns}\n'
|
||||
f'FallbackDNS={fallback_dns}\n'
|
||||
))
|
||||
self.addCleanup(os.remove, conf)
|
||||
subprocess.check_call(
|
||||
['systemctl', 'reload', 'systemd-resolved']
|
||||
)
|
||||
|
||||
self.start_unit('systemd-networkd')
|
||||
|
||||
env = os.environ.copy()
|
||||
env['SYSTEMD_LOG_LEVEL'] = 'debug'
|
||||
env['SYSTEMD_LOG_TARGET'] = 'console'
|
||||
r = subprocess.run(
|
||||
[NETWORKD_WAIT_ONLINE, '--dns', '--interface', iface, f'--timeout={timeout}'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env,
|
||||
)
|
||||
|
||||
if expect_timeout:
|
||||
self.assertNotEqual(
|
||||
r.returncode,
|
||||
0,
|
||||
f'Command succeeded unexpectedly:\n{r.stderr.decode()}'
|
||||
)
|
||||
self.assertRegex(
|
||||
r.stderr.decode(),
|
||||
rf'{iface}: No link-specific DNS server is accessible',
|
||||
f'Missing expected log message:\n{r.stderr.decode()}'
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
r.returncode,
|
||||
0,
|
||||
f'Command timed out:\n{r.stderr.decode()}'
|
||||
)
|
||||
|
||||
def test_wait_online_dns(self):
|
||||
''' test systemd-networkd-wait-online with --dns '''
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.do_test_wait_online_dns(self.iface)
|
||||
|
||||
def test_wait_online_dns_global(self):
|
||||
'''
|
||||
test systemd-networkd-wait-online with --dns, expect pass due to global DNS
|
||||
'''
|
||||
|
||||
# Explicitly set DNSDefaultRoute=no, and allow global DNS to be used.
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
'DNSDefaultRoute=no\n'
|
||||
'[DHCPv4]\n'
|
||||
'UseDNS=no\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.do_test_wait_online_dns(self.iface, global_dns='192.168.5.1')
|
||||
|
||||
def test_wait_online_dns_expect_timeout(self):
|
||||
''' test systemd-networkd-wait-online with --dns, and expect timeout '''
|
||||
|
||||
# Explicitly set DNSDefaultRoute=yes, and require link-specific DNS to be used.
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
'DNSDefaultRoute=yes\n'
|
||||
'[DHCPv4]\n'
|
||||
'UseDNS=no\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.do_test_wait_online_dns(self.iface, expect_timeout=True)
|
||||
|
||||
def test_wait_online_dns_expect_timeout_global(self):
|
||||
'''
|
||||
test systemd-networkd-wait-online with --dns, and expect timeout
|
||||
despite global DNS
|
||||
'''
|
||||
|
||||
# Configure Domains=~., and expect timeout despite global DNS servers
|
||||
# being available.
|
||||
self.write_network(
|
||||
self.config,
|
||||
(
|
||||
'[Match]\n'
|
||||
f'Name={self.iface}\n'
|
||||
'[Network]\n'
|
||||
'DHCP=ipv4\n'
|
||||
'Domains=~.\n'
|
||||
'[DHCPv4]\n'
|
||||
'UseDNS=no\n'
|
||||
)
|
||||
)
|
||||
self.create_iface()
|
||||
self.do_test_wait_online_dns(self.iface, expect_timeout=True, global_dns='192.168.5.1')
|
||||
|
||||
|
||||
class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
|
||||
"""Test [Match] sections in .network files.
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
(! systemd-run --wait -p DynamicUser=yes \
|
||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||
-p WorkingDirectory='~' true)
|
||||
|
||||
assert_eq "$(systemd-run --pipe --uid=root -p WorkingDirectory='~' pwd)" "/root"
|
||||
assert_eq "$(systemd-run --pipe --uid=nobody -p WorkingDirectory='~' pwd)" "/"
|
||||
assert_eq "$(systemd-run --pipe --uid=testuser -p WorkingDirectory='~' pwd)" "/home/testuser"
|
||||
|
||||
(! systemd-run --wait -p DynamicUser=yes -p User=testuser \
|
||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||
-p WorkingDirectory='~' true)
|
|
@ -934,6 +934,15 @@ testcase_11_nft() {
|
|||
|
||||
# Test resolvectl show-server-state
|
||||
testcase_12_resolvectl2() {
|
||||
# Cleanup
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
rm -f /run/systemd/resolved.conf.d/reload.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
}
|
||||
|
||||
trap cleanup RETURN
|
||||
|
||||
run resolvectl show-server-state
|
||||
grep -qF "10.0.0.1" "$RUN_OUT"
|
||||
grep -qF "Interface" "$RUN_OUT"
|
||||
|
@ -996,6 +1005,78 @@ testcase_12_resolvectl2() {
|
|||
restart_resolved
|
||||
}
|
||||
|
||||
# Test io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||
testcase_13_varlink_subscribe_dns_configuration() {
|
||||
# Cleanup
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
echo "===== io.systemd.Resolve.Monitor.SubscribeDNSConfiguration output: ====="
|
||||
cat "$tmpfile"
|
||||
echo "=========="
|
||||
rm -f /run/systemd/resolved.conf.d/global-dns.conf
|
||||
restart_resolved
|
||||
}
|
||||
|
||||
trap cleanup RETURN
|
||||
|
||||
local unit
|
||||
local tmpfile
|
||||
|
||||
unit="subscribe-dns-configuration-$(systemd-id128 new -u).service"
|
||||
tmpfile=$(mktemp)
|
||||
|
||||
# Clear global and per-interface DNS before monitoring the configuration change.
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
{
|
||||
echo "[Resolve]"
|
||||
echo "DNS="
|
||||
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
resolvectl dns dns0 ""
|
||||
|
||||
# Start the call to io.systemd.Resolve.Monitor.SubscribeDNSConfiguration
|
||||
systemd-run -u "$unit" -p "Type=exec" -p "StandardOutput=truncate:$tmpfile" \
|
||||
varlinkctl call --more --timeout=5 --graceful=io.systemd.TimedOut /run/systemd/resolve/io.systemd.Resolve.Monitor io.systemd.Resolve.Monitor.SubscribeDNSConfiguration '{}'
|
||||
|
||||
# Update the global configuration.
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
{
|
||||
echo "[Resolve]"
|
||||
echo "DNS=8.8.8.8"
|
||||
echo "Domains=lan"
|
||||
} > /run/systemd/resolved.conf.d/global-dns.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
|
||||
# Update a link configuration.
|
||||
resolvectl dns dns0 8.8.4.4 1.1.1.1
|
||||
|
||||
# Wait for the monitor to exit gracefully.
|
||||
while systemctl --quiet is-active "$unit"; do
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# Hack to remove the "Method call returned expected error" line from the output.
|
||||
sed -i '/^Method call.*returned expected error/d' "$tmpfile"
|
||||
|
||||
# Check that an initial reply was given with the settings applied BEFORE the monitor started.
|
||||
grep -qF \
|
||||
'{"global":{"servers":[],"domains":[]}}' \
|
||||
<(jq -cr --seq '.configuration[] | select(.ifname == null) | {"global": {servers: .servers, domains: .searchDomains}}' "$tmpfile")
|
||||
grep -qF \
|
||||
'{"dns0":{"servers":[],"domains":[]}}' \
|
||||
<(jq -cr --seq '.configuration[] | select(.ifname == "dns0") | {"dns0": {servers: .servers, domains: .searchDomains}}' "$tmpfile")
|
||||
|
||||
# Check that the global configuration change was reflected.
|
||||
grep -qF \
|
||||
'{"global":{"servers":[[8,8,8,8]],"domains":["lan"]}}' \
|
||||
<(jq -cr --seq '.configuration[] | select(.ifname == null) | {"global":{servers: [.servers[] | .address], domains: [.searchDomains[] | .name]}}' "$tmpfile")
|
||||
|
||||
# Check that the link configuration change was reflected.
|
||||
grep -qF \
|
||||
'{"dns0":{"servers":[[8,8,4,4],[1,1,1,1]],"domains":[]}}' \
|
||||
<(jq -cr --seq '.configuration[] | select(.ifname == "dns0") | {"dns0":{servers: [.servers[] | .address], domains: [.searchDomains[] | .name]}}' "$tmpfile")
|
||||
}
|
||||
|
||||
# PRE-SETUP
|
||||
systemctl unmask systemd-resolved.service
|
||||
systemctl enable --now systemd-resolved.service
|
||||
|
|
|
@ -16,6 +16,7 @@ ConditionDirectoryNotEmpty=|/run/confexts
|
|||
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/.extra/confext
|
||||
|
||||
DefaultDependencies=no
|
||||
After=local-fs.target
|
||||
|
|
|
@ -14,7 +14,7 @@ ConditionCapability=CAP_NET_ADMIN
|
|||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target
|
||||
BindsTo=systemd-networkd.service
|
||||
After=systemd-networkd.service
|
||||
After=systemd-networkd.service systemd-resolved.service
|
||||
Before=network-online.target shutdown.target
|
||||
|
||||
[Service]
|
||||
|
|
Loading…
Reference in New Issue