diff options
Diffstat (limited to 'src')
95 files changed, 3081 insertions, 983 deletions
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index d901a5e82b..1b816fb77a 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -62,16 +62,19 @@ static bool hostname_valid_char(char c) { } /** - * Check if s looks like a valid host name or fqdn. This does not do + * Check if s looks like a valid host name or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * relax is true and at least two components are present in the name. + * allow_trailing_dot is true and at least two components are present + * in the name. Note that due to the restricted charset and length + * this call is substantially more conservative than + * dns_domain_is_valid(). */ -bool hostname_is_valid(const char *s, bool relax) { +bool hostname_is_valid(const char *s, bool allow_trailing_dot) { + unsigned n_dots = 0; const char *p; bool dot; - unsigned dots = 0; if (isempty(s)) return false; @@ -87,7 +90,7 @@ bool hostname_is_valid(const char *s, bool relax) { return false; dot = true; - dots ++; + n_dots ++; } else { if (!hostname_valid_char(*p)) return false; @@ -96,10 +99,12 @@ bool hostname_is_valid(const char *s, bool relax) { } } - if (dot && (dots < 2 || !relax)) + if (dot && (n_dots < 2 || !allow_trailing_dot)) return false; - if (p-s > HOST_NAME_MAX) + if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on + * Linux, but DNS allows domain names + * up to 255 characters */ return false; return true; @@ -151,6 +156,17 @@ bool is_localhost(const char *hostname) { endswith_no_case(hostname, ".localdomain."); } +bool is_gateway_hostname(const char *hostname) { + assert(hostname); + + /* This tries to identify the valid syntaxes for the our + * synthetic "gateway" host. */ + + return + strcaseeq(hostname, "gateway") || + strcaseeq(hostname, "gateway."); +} + int sethostname_idempotent(const char *s) { char buf[HOST_NAME_MAX + 1] = {}; diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 6f2b5b66ff..d4f5bfe45e 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -29,10 +29,13 @@ bool hostname_is_set(void); char* gethostname_malloc(void); -bool hostname_is_valid(const char *s, bool relax) _pure_; +bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); +#define machine_name_is_valid(s) hostname_is_valid(s, false) + bool is_localhost(const char *hostname); +bool is_gateway_hostname(const char *hostname); int sethostname_idempotent(const char *s); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 12f1b193be..e278196c90 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -88,6 +88,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + + if (u == USEC_INFINITY) { + ts->realtime = ts->monotonic = USEC_INFINITY; + return ts; + } + ts->realtime = now(CLOCK_REALTIME); + ts->monotonic = now(CLOCK_MONOTONIC); + + delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; + + if ((int64_t) ts->realtime > delta) + ts->realtime -= delta; + else + ts->realtime = 0; + + if ((int64_t) ts->monotonic > delta) + ts->monotonic -= delta; + else + ts->monotonic = 0; + + return ts; +} + + usec_t timespec_load(const struct timespec *ts) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 7a64d454a0..2aba042217 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -74,6 +74,7 @@ usec_t now(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); static inline bool dual_timestamp_is_set(dual_timestamp *ts) { return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || diff --git a/src/basic/util.c b/src/basic/util.c index 9571f0ab12..f752595ca1 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -3006,21 +3006,6 @@ char* strshorten(char *s, size_t l) { return s; } -bool machine_name_is_valid(const char *s) { - - if (!hostname_is_valid(s, false)) - return false; - - /* Machine names should be useful hostnames, but also be - * useful in unit names, hence we enforce a stricter length - * limitation. */ - - if (strlen(s) > 64) - return false; - - return true; -} - int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, @@ -4928,6 +4913,9 @@ int container_get_leader(const char *machine, pid_t *pid) { assert(machine); assert(pid); + if (!machine_name_is_valid(machine)) + return -EINVAL; + p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) diff --git a/src/basic/util.h b/src/basic/util.h index 8f21d56ed8..1ead7b5419 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -394,8 +394,6 @@ bool nulstr_contains(const char*nulstr, const char *needle); bool plymouth_running(void); -bool machine_name_is_valid(const char *s) _pure_; - char* strshorten(char *s, size_t l); int symlink_idempotent(const char *from, const char *to); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a9f7971cde..3c98a0b186 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); @@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -932,6 +935,107 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "IgnoreSIGPIPE")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->ignore_sigpipe = b; + + unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "TTYVHangup")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_vhangup = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "TTYReset")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_reset = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "UtmpIdentifier")) { + const char *id; + + r = sd_bus_message_read(message, "s", &id); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(id)) + c->utmp_id = mfree(c->utmp_id); + else if (free_and_strdup(&c->utmp_id, id) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s\n", strempty(id)); + } + + return 1; + + } else if (streq(name, "UtmpMode")) { + const char *s; + ExecUtmpMode m; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + m = exec_utmp_mode_from_string(s); + if (m < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode"); + + if (mode != UNIT_CHECK) { + c->utmp_mode = m; + + unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s\n", exec_utmp_mode_to_string(m)); + } + + return 1; + + } else if (streq(name, "PAMName")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(n)) + c->pam_name = mfree(c->pam_name); + else if (free_and_strdup(&c->pam_name, n) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PAMName=%s\n", strempty(n)); + } + + return 1; + } else if (streq(name, "Environment")) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 1892725f91..0a9effda71 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -697,10 +697,40 @@ static int property_get_cpu_usage( return sd_bus_message_append(reply, "t", ns); } +static int property_get_cgroup( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + const char *t; + + assert(bus); + assert(reply); + assert(u); + + /* Three cases: a) u->cgroup_path is NULL, in which case the + * unit has no control group, which we report as the empty + * string. b) u->cgroup_path is the empty string, which + * indicates the root cgroup, which we report as "/". c) all + * other cases we report as-is. */ + + if (u->cgroup_path) + t = isempty(u->cgroup_path) ? "/" : u->cgroup_path; + else + t = ""; + + return sd_bus_message_append(reply, "s", t); +} + const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), - SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0), + SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_VTABLE_END diff --git a/src/core/execute.c b/src/core/execute.c index 3820165241..28eeeaad18 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -31,6 +31,7 @@ #include <grp.h> #include <poll.h> #include <glob.h> +#include <utmpx.h> #include <sys/personality.h> #ifdef HAVE_PAM @@ -1504,7 +1505,11 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); if (context->user && is_terminal_input(context->std_input)) { r = chown_terminal(STDIN_FILENO, uid); @@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/core/execute.h b/src/core/execute.h index f5d5c1dee7..8d14fe23d0 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters; #include "namespace.h" #include "bus-endpoint.h" +typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, + EXEC_UTMP_LOGIN, + EXEC_UTMP_USER, + _EXEC_UTMP_MODE_MAX, + _EXEC_UTMP_MODE_INVALID = -1 +} ExecUtmpMode; + typedef enum ExecInput { EXEC_INPUT_NULL, EXEC_INPUT_TTY, @@ -131,6 +139,7 @@ struct ExecContext { char *pam_name; char *utmp_id; + ExecUtmpMode utmp_mode; bool selinux_context_ignore; char *selinux_context; @@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_; const char* exec_input_to_string(ExecInput i) _const_; ExecInput exec_input_from_string(const char *s) _pure_; + +const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; +ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index fc6d2f4acb..2068ffd69b 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -112,7 +112,7 @@ int kmod_setup(void) { r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); if (r == 0) - log_info("Inserted module '%s'", kmod_module_get_name(mod)); + log_debug("Inserted module '%s'", kmod_module_get_name(mod)); else if (r == KMOD_PROBE_APPLY_BLACKLIST) log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); else { diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index aae81c80cb..60b97722db 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) $1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) m4_ifdef(`HAVE_SELINUX', `$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', `$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e1e3a5ffb7..b3bf8bdb40 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit, } #endif +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); int config_parse_kill_signal(const char *unit, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ce10d03c3f..fcca2b0221 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/import/export.c b/src/import/export.c index ec7dbe210a..b88d71fec6 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -24,6 +24,7 @@ #include "sd-event.h" #include "event-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "verbs.h" #include "build.h" #include "machine-image.h" diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 43cd413042..a27e81bbd4 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 2bf0b0680c..7ffe83cc33 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import.c b/src/import/import.c index b7772390e9..929a840298 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "import-tar.h" diff --git a/src/import/importd.c b/src/import/importd.c index dd314f5b00..8b508eaeec 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -35,6 +35,7 @@ #include "import-util.h" #include "process-util.h" #include "signal-util.h" +#include "hostname-util.h" typedef struct Transfer Transfer; typedef struct Manager Manager; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 5bfaf012c0..d0e0faa261 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -33,6 +33,7 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 71b8908a58..d38a2158c4 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -32,13 +32,14 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "process-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" #include "pull-job.h" #include "pull-common.h" #include "pull-tar.h" -#include "process-util.h" typedef enum TarProgress { TAR_DOWNLOADING, diff --git a/src/import/pull.c b/src/import/pull.c index ca7be6be85..e13cd6af97 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "pull-tar.h" diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c index 098f62af50..644ba91b0d 100644 --- a/src/journal/coredumpctl.c +++ b/src/journal/coredumpctl.c @@ -49,6 +49,7 @@ static enum { ACTION_GDB, } arg_action = ACTION_LIST; static const char* arg_field = NULL; +static const char *arg_directory = NULL; static int arg_no_pager = false; static int arg_no_legend = false; static int arg_one = false; @@ -131,6 +132,7 @@ static void help(void) { " -1 Show information about most recent entry only\n" " -F --field=FIELD List all values a certain field takes\n" " -o --output=FILE Write output to FILE\n\n" + " -D --directory=DIR Use journal files from directory\n\n" "Commands:\n" " list [MATCHES...] List available coredumps (default)\n" @@ -156,13 +158,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) { { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "output", required_argument, NULL, 'o' }, { "field", required_argument, NULL, 'F' }, + { "directory", required_argument, NULL, 'D' }, {} }; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0) switch(c) { case 'h': @@ -208,6 +211,10 @@ static int parse_argv(int argc, char *argv[], Set *matches) { arg_one = true; break; + case 'D': + arg_directory = optarg; + break; + case '?': return -EINVAL; @@ -808,10 +815,18 @@ int main(int argc, char *argv[]) { sigbus_install(); - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); - if (r < 0) { - log_error_errno(r, "Failed to open journal: %m"); - goto end; + if (arg_directory) { + r = sd_journal_open_directory(&j, arg_directory, 0); + if (r < 0) { + log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); + goto end; + } + } else { + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + log_error_errno(r, "Failed to open journal: %m"); + goto end; + } } /* We want full data, nothing truncated. */ diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index bfd1901e16..13fa9b52fc 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -43,6 +43,7 @@ #include "replace-var.h" #include "fileio.h" #include "formats-util.h" +#include "hostname-util.h" #define JOURNAL_FILES_MAX 7168 diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 4f54ad89a6..83e8192f58 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -5,7 +5,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -68,6 +68,11 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, DHCP6IA *ia); +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated); +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, + char ***str_arr); int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 109e0f4f21..037f580eb6 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -6,7 +6,7 @@ This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -40,6 +40,17 @@ struct sd_dhcp6_lease { DHCP6IA ia; DHCP6Address *addr_iter; + + struct in6_addr *dns; + size_t dns_count; + size_t dns_allocated; + char **domains; + size_t domains_count; + struct in6_addr *ntp; + size_t ntp_count; + size_t ntp_allocated; + char **ntp_fqdn; + size_t ntp_fqdn_count; }; int dhcp6_lease_clear_timers(DHCP6IA *ia); @@ -56,6 +67,13 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen); +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) ; + int dhcp6_lease_new(sd_dhcp6_lease **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index ea863f45e4..6da7ea7e27 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -26,9 +26,11 @@ #include "sparse-endian.h" #include "unaligned.h" #include "util.h" +#include "strv.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" +#include "dns-domain.h" #define DHCP6_OPTION_IA_NA_LEN 12 #define DHCP6_OPTION_IA_TA_LEN 4 @@ -317,3 +319,101 @@ error: return r; } + +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated) { + + if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) + return -EINVAL; + + if (!GREEDY_REALLOC(*addrs, *allocated, + count * sizeof(struct in6_addr) + optlen)) + return -ENOMEM; + + memcpy(*addrs + count, optval, optlen); + + count += optlen / sizeof(struct in6_addr); + + return count; +} + +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, + char ***str_arr) +{ + size_t pos = 0, idx = 0; + _cleanup_free_ char **names = NULL; + int r; + + assert_return(optlen > 1, -ENODATA); + assert_return(optval[optlen] == '\0', -EINVAL); + + while (pos < optlen) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + bool first = true; + + for (;;) { + uint8_t c; + + c = optval[pos++]; + + if (c == 0) + /* End of name */ + break; + else if (c <= 63) { + _cleanup_free_ char *t = NULL; + const char *label; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos > optlen) + return -EMSGSIZE; + + r = dns_label_escape(label, c, &t); + if (r < 0) + goto fail; + + if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) { + r = -ENOMEM; + goto fail; + } + + if (!first) + ret[n++] = '.'; + else + first = false; + + memcpy(ret + n, t, r); + n += r; + continue; + } else { + r = -EBADMSG; + goto fail; + } + } + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + ret[n] = 0; + + r = strv_extend(&names, ret); + if (r < 0) + goto fail; + + ret = NULL; + idx++; + } + + *str_arr = names; + names = NULL; + + return idx; + +fail: + return r; +} diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 3e0f339237..b3a28f88b4 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -123,7 +123,7 @@ enum { DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ - DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075 */ + DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ /* option code 35 is unassigned */ @@ -134,6 +134,12 @@ enum { }; enum { + DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, + DHCP6_NTP_SUBOPTION_MC_ADDR = 2, + DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, +}; + +enum { DHCP6_STATUS_SUCCESS = 0, DHCP6_STATUS_UNSPEC_FAIL = 1, DHCP6_STATUS_NO_ADDRS_AVAIL = 2, diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 3d78bf8b35..d8357c687e 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -32,6 +32,7 @@ #include "conf-parser.h" #include "condition.h" #include "network-internal.h" +#include "sd-icmp6-nd.h" const char *net_get_name(struct udev_device *device) { const char *name, *field; @@ -384,6 +385,20 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { return size; } +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, + size_t size) { + unsigned i; + + assert(f); + assert(addresses); + assert(size); + + for (i = 0; i < size; i++) + fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s", + SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]), + (i < (size - 1)) ? " ": ""); +} + int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { _cleanup_free_ struct in6_addr *addresses = NULL; int size = 0; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 7aaecbb10d..dca82646ce 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -67,6 +67,8 @@ const char *net_get_name(struct udev_device *device); void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size); int deserialize_in_addrs(struct in_addr **addresses, const char *string); +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, + size_t size); int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */ diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e2f5862851..bc17c6adc5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -73,6 +73,7 @@ static const uint16_t default_req_opts[] = { DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_NTP_SERVER, + DHCP6_OPTION_SNTP_SERVERS, }; const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { @@ -272,6 +273,11 @@ static void client_notify(sd_dhcp6_client *client, int event) { static int client_reset(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + if (client->lease) { + dhcp6_lease_clear_timers(&client->lease->ia); + client->lease = sd_dhcp6_lease_unref(client->lease); + } + client->receive_message = sd_event_source_unref(client->receive_message); @@ -748,7 +754,36 @@ static int client_parse_message(sd_dhcp6_client *client, return r; break; + + case DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_set_dns(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_set_domains(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_set_ntp(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_set_sntp(lease, optval, optlen); + if (r < 0) + return r; + + break; } + } if (r == -ENOMSG) @@ -802,10 +837,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, client->lease = sd_dhcp6_lease_unref(client->lease); } - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - client->lease = lease; - lease = NULL; - } + client->lease = lease; + lease = NULL; return DHCP6_STATE_BOUND; } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 2442269a3f..f0494b3c91 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -1,8 +1,10 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -20,9 +22,11 @@ #include <errno.h> +#include "strv.h" #include "util.h" #include "dhcp6-lease-internal.h" +#include "dhcp6-protocol.h" int dhcp6_lease_clear_timers(DHCP6IA *ia) { assert_return(ia, -EINVAL); @@ -173,6 +177,189 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia.addresses; } +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, + lease->dns_count, + &lease->dns_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid DNS server option: %s", + strerror(-r)); + + return r; + } + + lease->dns_count = r; + + return 0; +} + +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->dns_count) { + *addrs = lease->dns; + return lease->dns_count; + } + + return -ENOENT; +} + +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) { + int r; + char **domains; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_domainname(optval, optlen, &domains); + if (r < 0) + return 0; + + free(lease->domains); + lease->domains = domains; + lease->domains_count = r; + + return r; +} + +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { + assert_return(lease, -EINVAL); + assert_return(domains, -EINVAL); + + if (lease->domains_count) { + *domains = lease->domains; + return lease->domains_count; + } + + return -ENOENT; +} + +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) +{ + int r; + uint16_t subopt; + size_t sublen; + uint8_t *subval; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + free(lease->ntp); + lease->ntp_count = 0; + lease->ntp_allocated = 0; + + while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, + &subval)) >= 0) { + int s; + char **servers; + + switch(subopt) { + case DHCP6_NTP_SUBOPTION_SRV_ADDR: + case DHCP6_NTP_SUBOPTION_MC_ADDR: + if (sublen != 16) + return 0; + + s = dhcp6_option_parse_ip6addrs(subval, sublen, + &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (s < 0) + return s; + + lease->ntp_count = s; + + break; + + case DHCP6_NTP_SUBOPTION_SRV_FQDN: + r = dhcp6_option_parse_domainname(subval, sublen, + &servers); + if (r < 0) + return 0; + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); + lease->ntp_fqdn = servers; + lease->ntp_fqdn_count = r; + + break; + } + } + + if (r != -ENOMSG) + return r; + + return 0; +} + +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + if (lease->ntp || lease->ntp_fqdn) { + log_dhcp6_client(client, "NTP information already provided"); + + return 0; + } + + log_dhcp6_client(client, "Using deprecated SNTP information"); + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid SNTP server option: %s", + strerror(-r)); + + return r; + } + + lease->ntp_count = r; + + return 0; +} + +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->ntp_count) { + *addrs = lease->ntp; + return lease->ntp_count; + } + + return -ENOENT; +} + +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { + assert_return(lease, -EINVAL); + assert_return(ntp_fqdn, -EINVAL); + + if (lease->ntp_fqdn_count) { + *ntp_fqdn = lease->ntp_fqdn; + return lease->ntp_fqdn_count; + } + + return -ENOENT; +} + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -185,6 +372,13 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); + free(lease->dns); + + lease->domains = strv_free(lease->domains); + + free(lease->ntp); + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); free(lease); } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 761854714b..6e62262443 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -73,7 +73,7 @@ static int test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); @@ -216,6 +216,8 @@ static int test_advertise_option(sd_event *e) { uint32_t lt_pref, lt_valid; int r; bool opt_clientid = false; + struct in6_addr *addrs; + char **domains; if (verbose) printf("* %s\n", __FUNCTION__); @@ -276,6 +278,24 @@ static int test_advertise_option(sd_event *e) { break; + case DHCP6_OPTION_DNS_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_dns(lease, optval, + optlen) >= 0); + break; + + case DHCP6_OPTION_DOMAIN_LIST: + assert_se(optlen == 11); + assert_se(dhcp6_lease_set_domains(lease, optval, + optlen) >= 0); + break; + + case DHCP6_OPTION_SNTP_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_sntp(lease, optval, + optlen) >= 0); + break; + default: break; } @@ -315,6 +335,19 @@ static int test_advertise_option(sd_event *e) { assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); assert_se(preference == 0); + r = sd_dhcp6_lease_get_dns(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); + + r = sd_dhcp6_lease_get_domains(lease, &domains); + assert_se(r == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); + return 0; } @@ -339,10 +372,25 @@ int detect_virtualization(const char **id) { static void test_client_solicit_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; + sd_dhcp6_lease *lease; + struct in6_addr *addrs; + char **domains; assert_se(e); assert_se(event == DHCP6_EVENT_IP_ACQUIRE); + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); if (verbose) @@ -524,10 +572,25 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; + sd_dhcp6_lease *lease; + struct in6_addr *addrs; + char **domains; assert_se(e); assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST); + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + if (verbose) printf(" got DHCPv6 event %d\n", event); diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 92b12f2ae8..95c7d4ebe4 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -257,7 +257,7 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { KDBUS_FOREACH(name, name_list, cmd.list_size) { struct kdbus_item *item; - if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id) { + if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { char *n; if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 17ce297510..b2e617ada4 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1175,10 +1175,6 @@ static int process_get_managed_objects( if (bus->nodes_modified) return 0; - r = set_put_strdup(s, m->path); - if (r < 0) - return r; - r = sd_bus_message_new_method_return(m, &reply); if (r < 0) return r; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index db4f21e9ff..31cdcb4e5b 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -33,6 +33,7 @@ #include "missing.h" #include "def.h" #include "cgroup-util.h" +#include "hostname-util.h" #include "bus-label.h" #include "sd-bus.h" diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 1457759d0e..6b3bfad5b6 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -114,13 +114,14 @@ static int set_handler(sd_bus *bus, const char *path, const char *interface, con static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { _cleanup_free_ char *s = NULL; + const char *x; int r; assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0); r = sd_bus_message_append(reply, "s", s); assert_se(r >= 0); - assert_se(startswith(path, "/value/") != NULL || strcmp(path, "/value") == 0); + assert_se(x = startswith(path, "/value/")); assert_se(PTR_TO_UINT(userdata) == 30); diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 7fd77e9480..5eb37e16cb 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -719,6 +719,8 @@ static int parent_add_child(sd_device_enumerator *enumerator, const char *path) return r; r = sd_device_get_subsystem(device, &subsystem); + if (r == -ENOENT) + return 0; if (r < 0) return r; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 9bbf8974dd..0eadc8c747 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -32,6 +32,7 @@ #include "fileio.h" #include "login-util.h" #include "formats-util.h" +#include "hostname-util.h" #include "sd-login.h" _public_ int sd_pid_get_session(pid_t pid, char **session) { @@ -790,7 +791,7 @@ _public_ int sd_get_machine_names(char ***machines) { /* Filter out the unit: symlinks */ for (a = l, b = l; *a; a++) { - if (startswith(*a, "unit:")) + if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) free(*a); else { *b = *a; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 992a9f5b4a..5b2b36b9c0 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1339,7 +1339,8 @@ static int bus_manager_log_shutdown( InhibitWhat w, const char *unit_name) { - const char *p, *q; + const char *p; + const char *q; assert(m); assert(unit_name); @@ -1364,6 +1365,9 @@ static int bus_manager_log_shutdown( q = NULL; } + if (m->wall_message) + p = strjoina(p, " (", m->wall_message, ")", NULL); + return log_struct(LOG_NOTICE, LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), p, @@ -2282,6 +2286,44 @@ static int method_can_reboot_to_firmware_setup( return sd_bus_reply_method_return(message, "s", result); } +static int method_set_wall_message( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + Manager *m = userdata; + char *wall_message; + bool enable_wall_messages; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-wall-message", + false, + UID_INVALID, + &m->polkit_registry, + error); + + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = free_and_strdup(&m->wall_message, wall_message); + if (r < 0) + return log_oom(); + m->enable_wall_messages = enable_wall_messages; + + return sd_bus_reply_method_return(message, NULL); +} + static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; const char *who, *why, *what, *mode; @@ -2463,6 +2505,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("SessionNew", "so", 0), SD_BUS_SIGNAL("SessionRemoved", "so", 0), diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index d8deb7bc8b..1662d4c428 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -182,6 +182,10 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Manager" + send_member="SetWallMessage"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" send_member="AttachDevice"/> <allow send_destination="org.freedesktop.login1" diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index 83e7183323..23326bb79f 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -150,6 +150,7 @@ <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>yes</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate> </action> <action id="org.freedesktop.login1.power-off-multiple-sessions"> @@ -182,6 +183,7 @@ <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>yes</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate> </action> <action id="org.freedesktop.login1.reboot-multiple-sessions"> @@ -300,4 +302,14 @@ </defaults> </action> + <action id="org.freedesktop.login1.set-wall-message"> + <_description>Set a wall message</_description> + <_message>Authentication is required to set a wall message</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b62a9bd813..af2b8eff06 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -44,6 +44,7 @@ #include "machine-dbus.h" #include "formats-util.h" #include "process-util.h" +#include "env-util.h" static int property_get_id( sd_bus *bus, @@ -185,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; Machine *m = userdata; - const char *p; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); + r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + switch (m->class) { - if (child == 0) { + case MACHINE_HOST: { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1, -1); - if (r < 0) - _exit(EXIT_FAILURE); + int n, i; n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) - _exit(EXIT_FAILURE); + return n; for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - r = writev(pair[1], iov, 2); + r = sd_bus_message_open_container(reply, 'r', "iay"); if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); + return r; - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; + break; + } - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'r', "iay"); + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); if (r < 0) return r; - r = sd_bus_message_append(reply, "i", family); + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); if (r < 0) return r; - switch (family) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; + + pair[0] = safe_close(pair[0]); - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); + + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); + + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; + pair[1] = safe_close(pair[1]); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; + _exit(EXIT_SUCCESS); } - if (r < 0) - return r; - r = sd_bus_message_close_container(reply); + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); if (r < 0) - return r; + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + break; } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -330,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; Machine *m = userdata; char **k, **v; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + switch (m->class) { - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; + break; - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + siginfo_t si; + pid_t child; - if (child == 0) { - _cleanup_close_ int fd = -1; + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; - pair[0] = safe_close(pair[0]); + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int fd = -1; - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } + + r = copy_bytes(fd, pair[1], (off_t) -1, false); + if (r < 0) _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); + pair[1] = safe_close(pair[1]); - _exit(EXIT_SUCCESS); - } + f = fdopen(pair[0], "re"); + if (!f) + return -errno; - pair[1] = safe_close(pair[1]); + pair[0] = -1; - f = fdopen(pair[0], "re"); - if (!f) - return -errno; + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - pair[0] = -1; + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; + break; + } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -429,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines."); + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -451,26 +515,67 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_send(NULL, reply, NULL); } +static int container_bus_new(Machine *m, sd_bus **ret) { + int r; + + assert(m); + assert(ret); + + switch (m->class) { + + case MACHINE_HOST: + *ret = NULL; + break; + + case MACHINE_CONTAINER: { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } + + return 0; +} + int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = NULL, *getty = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; _cleanup_close_ int master = -1; + sd_bus *container_bus = NULL; Machine *m = userdata; - const char *p; - char *address; + const char *p, *getty; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.login", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", false, UID_INVALID, &m->manager->polkit_registry, @@ -480,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -495,26 +600,13 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = sd_bus_new(&container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; -# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI - if (asprintf(&address, ADDRESS_FMT, m->leader) < 0) - return log_oom(); - - container_bus->address = address; - container_bus->bus_client = true; - container_bus->trusted = false; - container_bus->is_system = true; + container_bus = allocated_bus ?: m->manager->bus; - r = sd_bus_start(container_bus); - if (r < 0) - return r; - - getty = strjoin("container-getty@", p, ".service", NULL); - if (!getty) - return log_oom(); + getty = strjoina("container-getty@", p, ".service"); r = sd_bus_call_method( container_bus, @@ -527,7 +619,228 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; + _cleanup_close_ int master = -1; + _cleanup_strv_free_ char **env = NULL, **args = NULL; + Machine *m = userdata; + const char *p, *unit, *user, *path, *description, *utmp_id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); + if (r < 0) + return r; + if (isempty(user)) + user = NULL; + if (isempty(path)) + path = "/bin/sh"; + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + + r = sd_bus_message_read_strv(message, &args); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + if (!strv_env_is_valid(env)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_malloc(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); + + utmp_id = path_startswith(pty_name, "/dev/"); + assert(utmp_id); + + if (unlockpt(master) < 0) + return -errno; + + r = container_bus_new(m, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + r = sd_bus_message_new_method_call( + container_bus, + &tm, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + /* Name and mode */ + unit = strjoina("container-shell@", p, ".service", NULL); + r = sd_bus_message_append(tm, "ss", unit, "fail"); + if (r < 0) + return r; + + /* Properties */ + r = sd_bus_message_open_container(tm, 'a', "(sv)"); + if (r < 0) + return r; + + description = strjoina("Shell for User ", isempty(user) ? "root" : user); + r = sd_bus_message_append(tm, + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "Description", "s", description, + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_name, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYVHangup", "b", true, + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); + if (r < 0) + return r; + + if (!strv_isempty(env)) { + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, env); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + } + + /* Exec container */ + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, args); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "b", true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + /* Auxiliary units */ + r = sd_bus_message_append(tm, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(container_bus, tm, 0, error, NULL); + if (r < 0) + return r; r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -966,8 +1279,9 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d309131860..38b46ad936 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index f045159d41..0d1b119dc1 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -37,12 +37,17 @@ #include "machine-dbus.h" #include "formats-util.h" -Machine* machine_new(Manager *manager, const char *name) { +Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; assert(manager); + assert(class < _MACHINE_CLASS_MAX); assert(name); + /* Passing class == _MACHINE_CLASS_INVALID here is fine. It + * means as much as "we don't know yet", and that we'll figure + * it out later when loading the state file. */ + m = new0(Machine, 1); if (!m) return NULL; @@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) { if (!m->name) goto fail; - m->state_file = strappend("/run/systemd/machines/", m->name); - if (!m->state_file) - goto fail; + if (class != MACHINE_HOST) { + m->state_file = strappend("/run/systemd/machines/", m->name); + if (!m->state_file) + goto fail; + } + + m->class = class; if (hashmap_put(manager->machines, m->name, m) < 0) goto fail; - m->class = _MACHINE_CLASS_INVALID; m->manager = manager; return m; @@ -86,6 +94,9 @@ void machine_free(Machine *m) { (void) hashmap_remove(m->manager->machines, m->name); + if (m->manager->host_machine == m) + m->manager->host_machine = NULL; + if (m->leader > 0) (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); @@ -105,7 +116,9 @@ int machine_save(Machine *m) { int r; assert(m); - assert(m->state_file); + + if (!m->state_file) + return 0; if (!m->started) return 0; @@ -244,6 +257,9 @@ int machine_load(Machine *m) { assert(m); + if (!m->state_file) + return 0; + r = parse_env_file(m->state_file, NEWLINE, "SCOPE", &m->unit, "SCOPE_JOB", &m->scope_job, @@ -325,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er int r = 0; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) { _cleanup_free_ char *escaped = NULL; @@ -364,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + if (m->started) return 0; @@ -402,6 +422,7 @@ static int machine_stop_scope(Machine *m) { int r; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) return 0; @@ -422,6 +443,9 @@ int machine_stop(Machine *m) { int r; assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + r = machine_stop_scope(m); m->stopping = true; @@ -456,6 +480,9 @@ int machine_finalize(Machine *m) { bool machine_check_gc(Machine *m, bool drop_not_started) { assert(m); + if (m->class == MACHINE_HOST) + return true; + if (drop_not_started && !m->started) return false; @@ -481,6 +508,9 @@ void machine_add_to_gc_queue(Machine *m) { MachineState machine_get_state(Machine *s) { assert(s); + if (s->class == MACHINE_HOST) + return MACHINE_RUNNING; + if (s->stopping) return MACHINE_CLOSING; @@ -493,6 +523,9 @@ MachineState machine_get_state(Machine *s) { int machine_kill(Machine *m, KillWho who, int signo) { assert(m); + if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) + return -EOPNOTSUPP; + if (!m->unit) return -ESRCH; @@ -509,6 +542,25 @@ int machine_kill(Machine *m, KillWho who, int signo) { return manager_kill_unit(m->manager, m->unit, signo, NULL); } +int machine_openpt(Machine *m, int flags) { + assert(m); + + switch (m->class) { + + case MACHINE_HOST: + return posix_openpt(flags); + + case MACHINE_CONTAINER: + if (m->leader <= 0) + return -EINVAL; + + return openpt_in_namespace(m->leader, flags); + + default: + return -EOPNOTSUPP; + } +} + MachineOperation *machine_operation_unref(MachineOperation *o) { if (!o) return NULL; @@ -544,7 +596,8 @@ void machine_release_unit(Machine *m) { static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", - [MACHINE_VM] = "vm" + [MACHINE_VM] = "vm", + [MACHINE_HOST] = "host", }; DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); diff --git a/src/machine/machine.h b/src/machine/machine.h index 0132b65a97..5f978289f2 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -39,6 +39,7 @@ typedef enum MachineState { typedef enum MachineClass { MACHINE_CONTAINER, MACHINE_VM, + MACHINE_HOST, _MACHINE_CLASS_MAX, _MACHINE_CLASS_INVALID = -1 } MachineClass; @@ -95,7 +96,7 @@ struct Machine { unsigned n_operations; }; -Machine* machine_new(Manager *manager, const char *name); +Machine* machine_new(Manager *manager, MachineClass class, const char *name); void machine_free(Machine *m); bool machine_check_gc(Machine *m, bool drop_not_started); void machine_add_to_gc_queue(Machine *m); @@ -120,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +int machine_openpt(Machine *m, int flags); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 66ed41087c..f9f49ee892 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -55,6 +55,8 @@ #include "process-util.h" #include "terminal-util.h" #include "signal-util.h" +#include "env-util.h" +#include "hostname-util.h" static char **arg_property = NULL; static bool arg_all = false; @@ -75,6 +77,8 @@ static bool arg_force = false; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static const char* arg_dkr_index_url = NULL; static const char* arg_format = NULL; +static const char *arg_uid = NULL; +static char **arg_setenv = NULL; static void pager_open_if_enabled(void) { @@ -154,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) { while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { size_t l; + if (name[0] == '.' && !arg_all) + continue; + if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) return log_oom(); @@ -354,7 +361,8 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", + endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : + endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", "ControlGroup", &error, &reply, @@ -368,9 +376,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (isempty(cgroup)) - return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) return 0; @@ -1170,20 +1175,72 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r return 0; } +static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) { + char last_char = 0; + bool machine_died; + int ret = 0, r; + + assert(event); + assert(master >= 0); + assert(name); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, ignore_vhangup, false, forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(*forward, &last_char); + + machine_died = + ignore_vhangup && + pty_forward_get_ignore_vhangup(*forward) == 0; + + *forward = pty_forward_free(*forward); + + if (last_char != '\n') + fputc('\n', stdout); + + if (machine_died) + log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); + else + log_info("Connection to machine %s terminated.", name); + + sd_event_get_exit_code(event, &ret); + return ret; +} + static int login_machine(int argc, char *argv[], void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; _cleanup_event_unref_ sd_event *event = NULL; - int master = -1, r, ret = 0; + int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match; - char last_char = 0; - bool machine_died; + const char *pty, *match, *machine; assert(bus); + if (!strv_isempty(arg_setenv) || arg_uid) { + log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); + return -EINVAL; + } + if (arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) { log_error("Login only supported on local machines."); @@ -1200,14 +1257,14 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1221,9 +1278,9 @@ static int login_machine(int argc, char *argv[], void *userdata) { "OpenMachineLogin", &error, &reply, - "s", argv[1]); + "s", machine); if (r < 0) { - log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); + log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); return r; } @@ -1231,36 +1288,100 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + return process_forward(event, &forward, master, true, machine); +} - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]); +static int shell_machine(int argc, char *argv[], void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_event_unref_ sd_event *event = NULL; + int master = -1, r; + sd_bus *bus = userdata; + const char *pty, *match, *machine, *path, *uid = NULL; - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + assert(bus); + + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Shell only supported on local machines."); + return -EOPNOTSUPP; + } + + polkit_agent_open_if_enabled(); - r = pty_forward_new(event, master, true, false, &forward); + r = sd_event_default(&event); if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); + return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_event_loop(event); + r = sd_bus_attach_event(bus, event, 0); if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); - pty_forward_get_last_char(forward, &last_char); - machine_died = pty_forward_get_ignore_vhangup(forward) == 0; + machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; - forward = pty_forward_free(forward); + if (arg_uid) + uid = arg_uid; + else if (machine) { + const char *at; - if (last_char != '\n') - fputc('\n', stdout); + at = strchr(machine, '@'); + if (at) { + uid = strndupa(machine, at - machine); + machine = at + 1; + } + } - if (machine_died) - log_info("Machine %s terminated.", argv[1]); - else - log_info("Connection to machine %s terminated.", argv[1]); + if (isempty(machine)) + machine = ".host"; - sd_event_get_exit_code(event, &ret); - return ret; + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); + + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineShell"); + if (r < 0) + return bus_log_create_error(r); + + path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; + + r = sd_bus_message_append(m, "sss", machine, uid, path); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_setenv); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, false, machine); } static int remove_image(int argc, char *argv[], void *userdata) { @@ -2314,6 +2435,8 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" + " --uid=USER Specify user ID to invoke shell as\n" + " --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2328,9 +2451,13 @@ static int help(int argc, char *argv[], void *userdata) { "Machine Commands:\n" " list List running VMs and containers\n" " status NAME... Show VM/container details\n" - " show NAME... Show properties of one or more VMs/containers\n" + " show [NAME...] Show properties of one or more VMs/containers\n" " start NAME... Start container as a service\n" - " login NAME Get a login prompt on a container\n" + " login [NAME] Get a login prompt in a container or on the\n" + " local host\n" + " shell [[USER@]NAME [COMMAND...]]\n" + " Invoke a shell (or other command) in a container\n" + " or on the local host\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" @@ -2342,8 +2469,8 @@ static int help(int argc, char *argv[], void *userdata) { " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" "Image Commands:\n" " list-images Show available container and VM images\n" - " image-status NAME... Show image details\n" - " show-image NAME... Show properties of image\n" + " image-status [NAME...] Show image details\n" + " show-image [NAME...] Show properties of image\n" " clone NAME NAME Clone an image\n" " rename NAME NAME Rename an image\n" " read-only NAME [BOOL] Mark or unmark image read-only\n" @@ -2378,6 +2505,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_DKR_INDEX_URL, ARG_FORMAT, + ARG_UID, + ARG_SETENV, }; static const struct option options[] = { @@ -2402,6 +2531,8 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL }, { "format", required_argument, NULL, ARG_FORMAT }, + { "uid", required_argument, NULL, ARG_UID }, + { "setenv", required_argument, NULL, ARG_SETENV }, {} }; @@ -2532,6 +2663,21 @@ static int parse_argv(int argc, char *argv[]) { arg_format = optarg; break; + case ARG_UID: + arg_uid = optarg; + break; + + case ARG_SETENV: + if (!env_assignment_is_valid(optarg)) { + log_error("Environment assignment invalid: %s", optarg); + return -EINVAL; + } + + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -2556,7 +2702,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "reboot", 2, VERB_ANY, 0, reboot_machine }, { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, { "kill", 2, VERB_ANY, 0, kill_machine }, - { "login", 2, 2, 0, login_machine }, + { "login", VERB_ANY, 2, 0, login_machine }, + { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, { "bind", 3, 4, 0, bind_mount }, { "copy-to", 3, 4, 0, copy_files }, { "copy-from", 3, 4, 0, copy_files }, @@ -2610,6 +2757,7 @@ finish: polkit_agent_close(); strv_free(arg_property); + strv_free(arg_setenv); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 08a7f58ef5..0d52c693e4 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -33,6 +33,7 @@ #include "btrfs-util.h" #include "formats-util.h" #include "process-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machine-pool.h" #include "image-dbus.h" @@ -637,6 +638,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd return bus_machine_method_open_login(message, machine, error); } +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; Machine *machine; @@ -860,6 +882,9 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "uid_map"); f = fopen(p, "re"); if (!f) @@ -912,6 +937,9 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); f = fopen(p, "re"); if (!f) { @@ -972,6 +1000,9 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "gid_map"); f = fopen(p, "re"); if (!f) @@ -1024,6 +1055,9 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); f = fopen(p, "re"); if (!f) { @@ -1085,6 +1119,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1455,7 +1490,6 @@ int manager_job_is_active(Manager *manager, const char *path) { } int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - _cleanup_free_ char *unit = NULL; Machine *mm; int r; @@ -1463,12 +1497,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { assert(pid >= 1); assert(machine); - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); - else - mm = hashmap_get(m->machine_units, unit); + mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); + if (!mm) { + _cleanup_free_ char *unit = NULL; + r = cg_pid_get_unit(pid, &unit); + if (r >= 0) + mm = hashmap_get(m->machine_units, unit); + } if (!mm) return 0; @@ -1484,7 +1520,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { machine = hashmap_get(m->machines, name); if (!machine) { - machine = machine_new(m, name); + machine = machine_new(m, _MACHINE_CLASS_INVALID, name); if (!machine) return -ENOMEM; } diff --git a/src/machine/machined.c b/src/machine/machined.c index c8ad157326..df3cc9972a 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -30,6 +30,7 @@ #include "label.h" #include "formats-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machined.h" @@ -89,6 +90,45 @@ void manager_free(Manager *m) { free(m); } +static int manager_add_host_machine(Manager *m) { + _cleanup_free_ char *rd = NULL, *unit = NULL; + sd_id128_t mid; + Machine *t; + int r; + + if (m->host_machine) + return 0; + + r = sd_id128_get_machine(&mid); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + rd = strdup("/"); + if (!rd) + return log_oom(); + + unit = strdup("-.slice"); + if (!unit) + return log_oom(); + + t = machine_new(m, MACHINE_HOST, ".host"); + if (!t) + return log_oom(); + + t->leader = 1; + t->id = mid; + + t->root_directory = rd; + t->unit = unit; + rd = unit = NULL; + + dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + + m->host_machine = t; + + return 0; +} + int manager_enumerate_machines(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -96,6 +136,10 @@ int manager_enumerate_machines(Manager *m) { assert(m); + r = manager_add_host_machine(m); + if (r < 0) + return r; + /* Read in machine data stored on disk */ d = opendir("/run/systemd/machines"); if (!d) { @@ -117,11 +161,12 @@ int manager_enumerate_machines(Manager *m) { if (startswith(de->d_name, "unit:")) continue; + if (!machine_name_is_valid(de->d_name)) + continue; + k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { - log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); - - r = k; + r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); continue; } diff --git a/src/machine/machined.h b/src/machine/machined.h index 61dbefb5f1..b3e59bf998 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -48,6 +48,8 @@ struct Manager { sd_event_source *image_cache_defer_event; LIST_HEAD(Machine, machine_gc_queue); + + Machine *host_machine; }; Manager *manager_new(void); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index d58f01507b..9d40b90151 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -70,6 +70,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Manager" + send_member="OpenMachineShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" send_member="TerminateMachine"/> <allow send_destination="org.freedesktop.machine1" @@ -142,6 +146,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Machine" + send_member="OpenShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Machine" send_member="Terminate"/> <allow send_destination="org.freedesktop.machine1" diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index 02714e83ae..69f78a5c25 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,58 @@ </defaults> </action> + <action id="org.freedesktop.machine1.host-login"> + <_description>Log into the local host</_description> + <_message>Authentication is required to log into the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.shell"> + <_description>Acquire a shell in a local container</_description> + <_message>Authentication is required to acquire a shell in a local container.</_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.imply">org.freedesktop.login1.login</annotate> + </action> + + <action id="org.freedesktop.machine1.host-shell"> + <_description>Acquire a shell on the local host</_description> + <_message>Authentication is required to acquire a shell on the local host.</_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.imply">org.freedesktop.login1.host-login</annotate> + </action> + + <action id="org.freedesktop.machine1.open-pty"> + <_description>Acquire a pseudo TTY in a local container</_description> + <_message>Authentication is required to acquire a pseudo TTY in a local container.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.host-open-pty"> + <_description>Acquire a pseudo TTY on the local host</_description> + <_message>Authentication is required to acquire a pseudo TTY on the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + <action id="org.freedesktop.machine1.manage-machines"> <_description>Manage local virtual machines and containers</_description> <_message>Authentication is required to manage local virtual machines and containers.</_message> @@ -34,6 +86,7 @@ <allow_inactive>auth_admin</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.shell org.freedesktop.login1.login</annotate> </action> <action id="org.freedesktop.machine1.manage-images"> diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 78e96c4e5b..cc9dc393c6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2240,6 +2240,14 @@ int link_save(Link *link) { if (link->network) { char **address, **domain; bool space; + sd_dhcp6_lease *dhcp6_lease = NULL; + + if (link->dhcp6_client) { + r = sd_dhcp6_client_get_lease(link->dhcp6_client, + &dhcp6_lease); + if (r < 0) + log_link_debug(link, "No DHCPv6 lease"); + } fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); @@ -2261,6 +2269,19 @@ int link_save(Link *link) { if (space) fputc(' ', f); serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_dns && dhcp6_lease) { + struct in6_addr *in6_addrs; + + r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; } } @@ -2284,6 +2305,32 @@ int link_save(Link *link) { if (space) fputc(' ', f); serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_ntp && dhcp6_lease) { + struct in6_addr *in6_addrs; + char **hosts; + char **hostname; + + r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease, + &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; + } + + r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts); + if (r > 0) { + STRV_FOREACH(hostname, hosts) { + if (space) + fputc(' ', f); + fputs(*hostname, f); + space = true; + } } } @@ -2307,6 +2354,21 @@ int link_save(Link *link) { if (space) fputc(' ', f); fputs(domainname, f); + space = true; + } + } + + if (link->network->dhcp_domains && dhcp6_lease) { + char **domains; + + r = sd_dhcp6_lease_get_domains(dhcp6_lease, &domains); + if (r >= 0) { + STRV_FOREACH(domain, domains) { + if (space) + fputc(' ', f); + fputs(*domain, f); + space = true; + } } } @@ -2316,7 +2378,7 @@ int link_save(Link *link) { yes_no(link->network->wildcard_domain)); fprintf(f, "LLMNR=%s\n", - llmnr_support_to_string(link->network->llmnr)); + resolve_support_to_string(link->network->llmnr)); } if (!hashmap_isempty(link->bound_to_links)) { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 8735b39581..7ac7ef1ea3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -45,7 +45,7 @@ Network.Address, config_parse_address, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, offsetof(Network, domains) Network.DNS, config_parse_strv, 0, offsetof(Network, dns) -Network.LLMNR, config_parse_llmnr, 0, offsetof(Network, llmnr) +Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr) Network.NTP, config_parse_strv, 0, offsetof(Network, ntp) Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward) Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 3f7e77da3e..6587ea994c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -111,7 +111,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->allow_port_to_be_root = true; network->unicast_flood = true; - network->llmnr = LLMNR_SUPPORT_YES; + network->llmnr = RESOLVE_SUPPORT_YES; network->link_local = ADDRESS_FAMILY_IPV6; @@ -632,15 +632,15 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); -static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = { - [LLMNR_SUPPORT_NO] = "no", - [LLMNR_SUPPORT_YES] = "yes", - [LLMNR_SUPPORT_RESOLVE] = "resolve", +static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { + [RESOLVE_SUPPORT_NO] = "no", + [RESOLVE_SUPPORT_YES] = "yes", + [RESOLVE_SUPPORT_RESOLVE] = "resolve", }; -DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport); +DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport); -int config_parse_llmnr( +int config_parse_resolve( const char* unit, const char *filename, unsigned line, @@ -652,32 +652,32 @@ int config_parse_llmnr( void *data, void *userdata) { - LLMNRSupport *llmnr = data; + ResolveSupport *resolve = data; int k; assert(filename); assert(lvalue); assert(rvalue); - assert(llmnr); + assert(resolve); /* Our enum shall be a superset of booleans, hence first try * to parse as boolean, and then as enum */ k = parse_boolean(rvalue); if (k > 0) - *llmnr = LLMNR_SUPPORT_YES; + *resolve = RESOLVE_SUPPORT_YES; else if (k == 0) - *llmnr = LLMNR_SUPPORT_NO; + *resolve = RESOLVE_SUPPORT_NO; else { - LLMNRSupport s; + ResolveSupport s; - s = llmnr_support_from_string(rvalue); + s = resolve_support_from_string(rvalue); if (s < 0){ - log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s option, ignoring: %s", lvalue, rvalue); return 0; } - *llmnr = s; + *resolve = s; } return 0; diff --git a/src/network/networkd.h b/src/network/networkd.h index a285a4b08f..5340922bf1 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -64,13 +64,13 @@ typedef enum AddressFamilyBoolean { _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, } AddressFamilyBoolean; -typedef enum LLMNRSupport { - LLMNR_SUPPORT_NO, - LLMNR_SUPPORT_YES, - LLMNR_SUPPORT_RESOLVE, - _LLMNR_SUPPORT_MAX, - _LLMNR_SUPPORT_INVALID = -1, -} LLMNRSupport; +typedef enum ResolveSupport { + RESOLVE_SUPPORT_NO, + RESOLVE_SUPPORT_YES, + RESOLVE_SUPPORT_RESOLVE, + _RESOLVE_SUPPORT_MAX, + _RESOLVE_SUPPORT_INVALID = -1, +} ResolveSupport; typedef enum LinkOperationalState { LINK_OPERSTATE_OFF, @@ -178,7 +178,7 @@ struct Network { bool wildcard_domain; char **domains, **dns, **ntp, **bind_carrier; - LLMNRSupport llmnr; + ResolveSupport llmnr; LIST_FIELDS(Network, networks); }; @@ -421,14 +421,14 @@ int config_parse_ipv6token(const char *unit, const char *filename, unsigned line const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -/* LLMNR support */ +/* Resolve protocols support */ -const char* llmnr_support_to_string(LLMNRSupport i) _const_; -LLMNRSupport llmnr_support_from_string(const char *s) _pure_; +const char* resolve_support_to_string(ResolveSupport i) _const_; +ResolveSupport resolve_support_from_string(const char *s) _pure_; -int config_parse_llmnr(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_resolve(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); /* Address Pool */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 347260013a..837947ee28 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -559,9 +559,9 @@ static int parse_argv(int argc, char *argv[]) { break; case 'M': - if (isempty(optarg)) { + if (isempty(optarg)) arg_machine = mfree(arg_machine); - } else { + else { if (!machine_name_is_valid(optarg)) { log_error("Invalid machine name: %s", optarg); return -EINVAL; @@ -658,7 +658,6 @@ static int parse_argv(int argc, char *argv[]) { const char *current = optarg; _cleanup_free_ char *source = NULL, *destination = NULL; CustomMount *m; - _cleanup_strv_free_ char **strv = NULL; r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); switch (r) { @@ -4003,6 +4002,17 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo static int determine_names(void) { int r; + if (arg_template && !arg_directory && arg_machine) { + + /* If --template= was specified then we should not + * search for a machine, but instead create a new one + * in /var/lib/machine. */ + + arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL); + if (!arg_directory) + return log_oom(); + } + if (!arg_image && !arg_directory) { if (arg_machine) { _cleanup_(image_unrefp) Image *i = NULL; diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 69069cc75d..0dca891447 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -43,13 +43,6 @@ NSS_GETHOSTBYNAME_PROTOTYPES(myhostname); NSS_GETHOSTBYADDR_PROTOTYPES(myhostname); -static bool is_gateway(const char *hostname) { - assert(hostname); - - return streq(hostname, "gateway") || - streq(hostname, "gateway."); -} - enum nss_status _nss_myhostname_gethostbyname4_r( const char *name, struct gaih_addrtuple **pat, @@ -81,7 +74,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( canonical = "localhost"; local_address_ipv4 = htonl(INADDR_LOOPBACK); - } else if (is_gateway(name)) { + } else if (is_gateway_hostname(name)) { n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); if (n_addresses <= 0) { @@ -351,7 +344,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( canonical = "localhost"; local_address_ipv4 = htonl(INADDR_LOOPBACK); - } else if (is_gateway(name)) { + } else if (is_gateway_hostname(name)) { n_addresses = local_gateways(NULL, 0, af, &addresses); if (n_addresses <= 0) { diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index cdec83d074..5758ea1569 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -30,6 +30,7 @@ #include "bus-util.h" #include "bus-common-errors.h" #include "in-addr-util.h" +#include "hostname-util.h" NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index da22f98eba..ef5eb7b4cf 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -61,23 +61,21 @@ static bool bus_error_shall_fallback(sd_bus_error *e) { } static int count_addresses(sd_bus_message *m, int af, const char **canonical) { - int c = 0, r, ifindex; + int c = 0, r; assert(m); assert(canonical); - r = sd_bus_message_read(m, "i", &ifindex); + r = sd_bus_message_enter_container(m, 'a', "(iiay)"); if (r < 0) return r; - r = sd_bus_message_enter_container(m, 'a', "(iay)"); - if (r < 0) - return r; + while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { + int family, ifindex; - while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) { - int family; + assert_cc(sizeof(int32_t) == sizeof(int)); - r = sd_bus_message_read(m, "i", &family); + r = sd_bus_message_read(m, "ii", &ifindex, &family); if (r < 0) return r; @@ -126,7 +124,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( const char *canonical = NULL; size_t l, ms, idx; char *r_name; - int c, r, i = 0, ifindex; + int c, r, i = 0; assert(name); assert(pat); @@ -218,28 +216,26 @@ enum nss_status _nss_resolve_gethostbyname4_r( /* Second, append addresses */ r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) goto fail; - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int family, ifindex; const void *a; size_t sz; - r = sd_bus_message_read(reply, "i", &family); + 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; @@ -308,7 +304,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; size_t l, idx, ms, alen; const char *canonical; - int c, r, i = 0, ifindex; + int c, r, i = 0; assert(name); assert(result); @@ -420,28 +416,24 @@ enum nss_status _nss_resolve_gethostbyname3_r( /* Third, append addresses */ r_addr = buffer + idx; - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) goto fail; - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int ifindex, family; const void *a; size_t sz; - r = sd_bus_message_read(reply, "i", &family); + 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; @@ -603,20 +595,17 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_UNAVAIL; } - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(is)"); if (r < 0) goto fail; - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - r = sd_bus_message_enter_container(reply, 'a', "s"); - if (r < 0) - goto fail; + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { c++; ms += ALIGN(strlen(n) + 1); } @@ -661,7 +650,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( /* Fourth, place aliases */ i = 0; r_name = buffer + idx; - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { char *p; size_t l; diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 7525d7c32f..9847effb53 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -41,13 +41,13 @@ static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; -static void print_source(int ifindex, uint64_t flags, usec_t rtt) { +static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; if (!arg_legend) return; - if (ifindex <= 0 && flags == 0) + if (flags == 0) return; fputs("\n-- Information acquired via", stdout); @@ -58,11 +58,6 @@ static void print_source(int ifindex, uint64_t flags, usec_t rtt) { flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : ""); - if (ifindex > 0) { - char ifname[IF_NAMESIZE] = ""; - printf(" interface %s", strna(if_indextoname(ifindex, ifname))); - } - assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); printf(" in %s", rtt_str); @@ -76,14 +71,18 @@ static int resolve_host(sd_bus *bus, const char *name) { _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *canonical = NULL; + char ifname[IF_NAMESIZE] = ""; unsigned c = 0; - int r, ifindex; + int r; uint64_t flags; usec_t ts; assert(name); - log_debug("Resolving %s (family %s, ifindex %i).", name, af_to_name(arg_family) ?: "*", arg_ifindex); + if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); + + log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname); r = sd_bus_message_new_method_call( bus, @@ -109,22 +108,19 @@ static int resolve_host(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { const void *a; - int family; size_t sz; _cleanup_free_ char *pretty = NULL; - char ifname[IF_NAMESIZE] = ""; + int ifindex, family; + + assert_cc(sizeof(int) == sizeof(int32_t)); - r = sd_bus_message_read(reply, "i", &family); + r = sd_bus_message_read(reply, "ii", &ifindex, &family); if (r < 0) return bus_log_parse_error(r); @@ -142,26 +138,17 @@ static int resolve_host(sd_bus *bus, const char *name) { } if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", - name, sz, af_to_name(family) ?: "unknown"); + log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); continue; } - if (ifindex > 0) { - char *t; - - t = if_indextoname(ifindex, ifname); - if (!t) { - log_error("Failed to resolve interface name for index %i", ifindex); - continue; - } - } + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); r = in_addr_to_string(family, a, &pretty); - if (r < 0) { - log_error_errno(r, "%s: failed to print address: %m", name); - continue; - } + if (r < 0) + return log_error_errno(r, "Failed to print address for %s: %m", name); printf("%*s%s %s%s%s\n", (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", @@ -192,7 +179,7 @@ static int resolve_host(sd_bus *bus, const char *name) { return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } @@ -204,7 +191,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a char ifname[IF_NAMESIZE] = ""; uint64_t flags; unsigned c = 0; - const char *n; usec_t ts; int r; @@ -212,19 +198,15 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); + if (ifindex <= 0) + ifindex = arg_ifindex; + r = in_addr_to_string(family, address, &pretty); if (r < 0) return log_oom(); - if (ifindex > 0) { - char *t; - - t = if_indextoname(ifindex, ifname); - if (!t) { - log_error("Failed to resolve interface name for index %i", ifindex); - return -errno; - } - } + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname); @@ -238,10 +220,6 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_append(req, "ii", ifindex, family); if (r < 0) return bus_log_create_error(r); @@ -264,19 +242,31 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'a', "s"); + r = sd_bus_message_enter_container(reply, 'a', "(is)"); if (r < 0) return bus_log_create_error(r); - while ((r = sd_bus_message_read(reply, "s", &n)) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) { + const char *n; + + assert_cc(sizeof(int) == sizeof(int32_t)); - printf("%*s%s%s%s %s\n", + r = sd_bus_message_read(reply, "is", &ifindex, &n); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%*s%*s%*s%s %s\n", (int) strlen(pretty), c == 0 ? pretty : "", - isempty(ifname) ? "" : "%", ifname, + isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%", + (int) strlen(ifname), c == 0 ? ifname : "", c == 0 ? ":" : " ", n); @@ -298,7 +288,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } @@ -333,14 +323,18 @@ static int resolve_record(sd_bus *bus, const char *name) { _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + char ifname[IF_NAMESIZE] = ""; unsigned n = 0; uint64_t flags; - int r, ifindex; + int r; usec_t ts; assert(name); - log_debug("Resolving %s %s %s.", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type)); + if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); + + log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname); r = sd_bus_message_new_method_call( bus, @@ -367,23 +361,22 @@ static int resolve_record(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC) - ts; - r = sd_bus_message_read(reply, "i", &ifindex); + r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_enter_container(reply, 'a', "(qqay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "qqay")) > 0) { + while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; _cleanup_free_ char *s = NULL; uint16_t c, t; + int ifindex; const void *d; size_t l; - r = sd_bus_message_read(reply, "qq", &c, &t); + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); if (r < 0) return bus_log_parse_error(r); @@ -399,6 +392,8 @@ static int resolve_record(sd_bus *bus, const char *name) { if (r < 0) return log_oom(); + p->refuse_compression = true; + r = dns_packet_append_blob(p, d, l, NULL); if (r < 0) return log_oom(); @@ -415,7 +410,11 @@ static int resolve_record(sd_bus *bus, const char *name) { return r; } - printf("%s\n", s); + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); n++; } if (r < 0) @@ -434,7 +433,7 @@ static int resolve_record(sd_bus *bus, const char *name) { return -ESRCH; } - print_source(ifindex, flags, ts); + print_source(flags, ts); return 0; } @@ -444,7 +443,7 @@ static void help_dns_types(void) { const char *t; if (arg_legend) - puts("Known dns types:"); + puts("Known DNS RR types:"); for (i = 0; i < _DNS_TYPE_MAX; i++) { t = dns_type_to_string(i); if (t) @@ -457,7 +456,7 @@ static void help_dns_classes(void) { const char *t; if (arg_legend) - puts("Known dns classes:"); + puts("Known DNS RR classes:"); for (i = 0; i < _DNS_CLASS_MAX; i++) { t = dns_class_to_string(i); if (t) @@ -491,7 +490,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "type", required_argument, NULL, 't' }, { "class", required_argument, NULL, 'c' }, - { "legend", optional_argument, NULL, ARG_LEGEND }, + { "legend", optional_argument, NULL, ARG_LEGEND }, { "protocol", required_argument, NULL, 'p' }, {} }; @@ -521,11 +520,21 @@ static int parse_argv(int argc, char *argv[]) { arg_family = AF_INET6; break; - case 'i': - arg_ifindex = if_nametoindex(optarg); - if (arg_ifindex <= 0) - return log_error_errno(errno, "Unknown interfaces %s: %m", optarg); + case 'i': { + int ifi; + + if (safe_atoi(optarg, &ifi) >= 0 && ifi > 0) + arg_ifindex = ifi; + else { + ifi = if_nametoindex(optarg); + if (ifi <= 0) + return log_error_errno(errno, "Unknown interface %s: %m", optarg); + + arg_ifindex = ifi; + } + break; + } case 't': if (streq(optarg, "help")) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 6db12511f9..12c17003e9 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -91,13 +91,17 @@ static int reply_query_state(DnsQuery *q) { } } -static int append_address(sd_bus_message *reply, DnsResourceRecord *rr) { +static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) { int r; assert(reply); assert(rr); - r = sd_bus_message_open_container(reply, 'r', "iay"); + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", ifindex); if (r < 0) return r; @@ -145,11 +149,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(iay)"); + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); if (r < 0) goto finish; @@ -157,27 +157,27 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) { /* Hmm, if this is not an address record, maybe it's a cname? If so, remember this */ - r = dns_question_matches_cname(q->question, answer->rrs[i]); + r = dns_question_matches_cname(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r > 0) - cname = dns_resource_record_ref(answer->rrs[i]); + cname = dns_resource_record_ref(answer->items[i].rr); continue; } - r = append_address(reply, answer->rrs[i]); + r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); if (r < 0) goto finish; if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); + canonical = dns_resource_record_ref(answer->items[i].rr); added ++; } @@ -204,18 +204,18 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { /* Before we restart the query, let's see if any of * the RRs we already got already answers our query */ for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) continue; - r = append_address(reply, answer->rrs[i]); + r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); if (r < 0) goto finish; if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); + canonical = dns_resource_record_ref(answer->items[i].rr); added++; } @@ -226,10 +226,6 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { * query, this time with the cname */ if (added <= 0) { r = dns_query_go(q); - if (r == -ESRCH) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - goto finish; - } if (r < 0) { r = sd_bus_reply_method_errno(q->request, -r, NULL); goto finish; @@ -346,10 +342,6 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } @@ -373,11 +365,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "s"); + r = sd_bus_message_open_container(reply, 'a', "(is)"); if (r < 0) goto finish; @@ -385,13 +373,13 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { answer = dns_answer_ref(q->answer); for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) continue; - r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name); + r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name); if (r < 0) goto finish; @@ -498,10 +486,6 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } @@ -525,11 +509,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(qqay)"); + r = sd_bus_message_open_container(reply, 'a', "(iqqay)"); if (r < 0) goto finish; @@ -540,7 +520,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; size_t start; - r = dns_question_matches_rr(q->question, answer->rrs[i]); + r = dns_question_matches_rr(q->question, answer->items[i].rr); if (r < 0) goto finish; if (r == 0) @@ -550,15 +530,20 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = dns_packet_append_rr(p, answer->rrs[i], &start); + p->refuse_compression = true; + + r = dns_packet_append_rr(p, answer->items[i].rr, &start); if (r < 0) goto finish; - r = sd_bus_message_open_container(reply, 'r', "qqay"); + r = sd_bus_message_open_container(reply, 'r', "iqqay"); if (r < 0) goto finish; - r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type); + r = sd_bus_message_append(reply, "iqq", + answer->items[i].ifindex, + answer->items[i].rr->key->class, + answer->items[i].rr->key->type); if (r < 0) goto finish; @@ -650,10 +635,6 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } @@ -662,9 +643,9 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ResolveHostname", "isit", "ia(iay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveAddress", "iiayt", "iast", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveRecord", "isqqt", "ia(qqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index f77b98e505..13ad4ca6bd 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -25,7 +25,7 @@ DnsAnswer *dns_answer_new(unsigned n) { DnsAnswer *a; - a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n); + a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n); if (!a) return NULL; @@ -54,7 +54,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) { unsigned i; for (i = 0; i < a->n_rrs; i++) - dns_resource_record_unref(a->rrs[i]); + dns_resource_record_unref(a->items[i].rr); free(a); } else @@ -63,25 +63,30 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) { return NULL; } -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { +int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) { unsigned i; int r; - assert(a); assert(rr); + if (!a) + return -ENOSPC; + for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_record_equal(a->rrs[i], rr); + if (a->items[i].ifindex != ifindex) + continue; + + r = dns_resource_record_equal(a->items[i].rr, rr); if (r < 0) return r; if (r > 0) { /* Entry already exists, keep the entry with * the higher RR, or the one with TTL 0 */ - if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) { + if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) { dns_resource_record_ref(rr); - dns_resource_record_unref(a->rrs[i]); - a->rrs[i] = rr; + dns_resource_record_unref(a->items[i].rr); + a->items[i].rr = rr; } return 0; @@ -91,7 +96,10 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { if (a->n_rrs >= a->n_allocated) return -ENOSPC; - a->rrs[a->n_rrs++] = dns_resource_record_ref(rr); + a->items[a->n_rrs].rr = dns_resource_record_ref(rr); + a->items[a->n_rrs].ifindex = ifindex; + a->n_rrs++; + return 1; } @@ -118,18 +126,20 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) { soa->soa.expire = 1; soa->soa.minimum = ttl; - return dns_answer_add(a, soa); + return dns_answer_add(a, soa, 0); } int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { unsigned i; int r; - assert(a); assert(key); + if (!a) + return 0; + for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_key_match_rr(key, a->rrs[i]); + r = dns_resource_key_match_rr(key, a->items[i].rr); if (r < 0) return r; if (r > 0) @@ -142,24 +152,26 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) { unsigned i; - assert(a); assert(key); assert(ret); + if (!a) + return 0; + /* For a SOA record we can never find a matching SOA record */ if (key->type == DNS_TYPE_SOA) return 0; for (i = 0; i < a->n_rrs; i++) { - if (a->rrs[i]->key->class != DNS_CLASS_IN) + if (a->items[i].rr->key->class != DNS_CLASS_IN) continue; - if (a->rrs[i]->key->type != DNS_TYPE_SOA) + if (a->items[i].rr->key->type != DNS_TYPE_SOA) continue; - if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) { - *ret = a->rrs[i]; + if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) { + *ret = a->items[i].rr; return 1; } } @@ -184,7 +196,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { if (a) { for (i = 0; i < a->n_rrs; i++) { - r = dns_answer_add(ret, a->rrs[i]); + r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex); if (r < 0) return NULL; } @@ -192,7 +204,7 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { if (b) { for (i = 0; i < b->n_rrs; i++) { - r = dns_answer_add(ret, b->rrs[i]); + r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex); if (r < 0) return NULL; } @@ -205,9 +217,11 @@ DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { } void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) { - DnsResourceRecord **rrs; + DnsAnswerItem *items; unsigned i, start, end; - assert(a); + + if (!a) + return; if (a->n_rrs <= 1) return; @@ -218,19 +232,51 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) { /* RFC 4795, Section 2.6 suggests we should order entries * depending on whether the sender is a link-local address. */ - rrs = newa(DnsResourceRecord*, a->n_rrs); + items = newa(DnsAnswerItem, a->n_rrs); for (i = 0; i < a->n_rrs; i++) { - if (a->rrs[i]->key->class == DNS_CLASS_IN && - ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) || - (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local))) + if (a->items[i].rr->key->class == DNS_CLASS_IN && + ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) || + (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local))) /* Order address records that are are not preferred to the end of the array */ - rrs[end--] = a->rrs[i]; + items[end--] = a->items[i]; else /* Order all other records to the beginning of the array */ - rrs[start++] = a->rrs[i]; + items[start++] = a->items[i]; } assert(start == end+1); - memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs); + memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs); +} + +int dns_answer_reserve(DnsAnswer **a, unsigned n_free) { + DnsAnswer *n; + + if (n_free <= 0) + return 0; + + if (*a) { + unsigned ns; + + if ((*a)->n_ref > 1) + return -EBUSY; + + ns = (*a)->n_rrs + n_free; + + if ((*a)->n_allocated >= ns) + return 0; + + n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns); + if (!n) + return -ENOMEM; + + n->n_allocated = ns; + } else { + n = dns_answer_new(n_free); + if (!n) + return -ENOMEM; + } + + *a = n; + return 0; } diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index af3e462ed5..0757dd60d0 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -22,22 +22,31 @@ ***/ typedef struct DnsAnswer DnsAnswer; +typedef struct DnsAnswerItem DnsAnswerItem; #include "resolved-dns-rr.h" -/* A simple array of resource records */ +/* A simple array of resource records. We keep track of the + * originating ifindex for each RR where that makes sense, so that we + * can qualify A and AAAA RRs referring to a local link with the + * right ifindex. */ + +struct DnsAnswerItem { + DnsResourceRecord *rr; + int ifindex; +}; struct DnsAnswer { unsigned n_ref; unsigned n_rrs, n_allocated; - DnsResourceRecord* rrs[0]; + DnsAnswerItem items[0]; }; DnsAnswer *dns_answer_new(unsigned n); DnsAnswer *dns_answer_ref(DnsAnswer *a); DnsAnswer *dns_answer_unref(DnsAnswer *a); -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr); +int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex); int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl); int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key); int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret); @@ -45,4 +54,6 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b); void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local); +int dns_answer_reserve(DnsAnswer **a, unsigned n_free); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 81ea1cafcb..ef6b69c768 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -411,22 +411,23 @@ int dns_cache_put( int owner_family, const union in_addr_union *owner_address) { - unsigned i; + unsigned cache_keys, i; int r; assert(c); - assert(q); - /* First, delete all matching old RRs, so that we only keep - * complete by_key in place. */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + /* First, if we were passed a question, delete all matching old RRs, + * so that we only keep complete by_key in place. */ + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } if (!answer) return 0; for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); /* We only care for positive replies and NXDOMAINs, on all * other replies we will simply flush the respective entries, @@ -435,19 +436,27 @@ int dns_cache_put( if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) return 0; + cache_keys = answer->n_rrs; + + if (q) + cache_keys += q->n_keys; + /* Make some space for our new entries */ - dns_cache_make_space(c, answer->n_rrs + q->n_keys); + dns_cache_make_space(c, cache_keys); if (timestamp <= 0) timestamp = now(clock_boottime_or_monotonic()); /* Second, add in positive entries for all contained RRs */ for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address); + r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address); if (r < 0) goto fail; } + if (!q) + return 0; + /* Third, add in negative entries for all keys with no RR */ for (i = 0; i < q->n_keys; i++) { DnsResourceRecord *soa = NULL; @@ -458,6 +467,10 @@ int dns_cache_put( if (r > 0) continue; + /* See https://tools.ietf.org/html/rfc2308, which + * say that a matching SOA record in the packet + * is used to to enable negative caching. */ + r = dns_answer_find_soa(answer, q->keys[i], &soa); if (r < 0) goto fail; @@ -475,80 +488,76 @@ fail: /* Adding all RRs failed. Let's clean up what we already * added, just in case */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } + for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); + dns_cache_remove(c, answer->items[i].rr->key); return r; } -int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { +int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned i, n = 0; + unsigned n = 0; int r; bool nxdomain = false; + _cleanup_free_ char *key_str = NULL; + DnsCacheItem *j, *first; assert(c); - assert(q); + assert(key); assert(rcode); assert(ret); - if (q->n_keys <= 0) { - *ret = NULL; - *rcode = 0; - return 0; - } + if (key->type == DNS_TYPE_ANY || + key->class == DNS_CLASS_ANY) { - for (i = 0; i < q->n_keys; i++) { - _cleanup_free_ char *key_str = NULL; - DnsCacheItem *j; + /* If we have ANY lookups we simply refresh */ - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - /* If we have ANY lookups we simply refresh */ + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - r = dns_resource_key_to_string(q->keys[i], &key_str); - if (r < 0) - return r; + log_debug("Ignoring cache for ANY lookup: %s", key_str); - log_debug("Ignoring cache for ANY lookup: %s", key_str); + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } - *ret = NULL; - *rcode = 0; - return 0; - } + first = hashmap_get(c->by_key, key); + if (!first) { + /* If one question cannot be answered we need to refresh */ - j = hashmap_get(c->by_key, q->keys[i]); - if (!j) { - /* If one question cannot be answered we need to refresh */ + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - r = dns_resource_key_to_string(q->keys[i], &key_str); - if (r < 0) - return r; + log_debug("Cache miss for %s", key_str); - log_debug("Cache miss for %s", key_str); + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } - *ret = NULL; - *rcode = 0; - return 0; - } else { - r = dns_resource_key_to_string(j->key, &key_str); - if (r < 0) - return r; + LIST_FOREACH(by_key, j, first) { + if (j->rr) + n++; + else if (j->type == DNS_CACHE_NXDOMAIN) + nxdomain = true; + } - log_debug("%s cache hit for %s", - j->type == DNS_CACHE_POSITIVE ? "Positive" : - (j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN"), key_str); - } + r = dns_resource_key_to_string(key, &key_str); + if (r < 0) + return r; - LIST_FOREACH(by_key, j, j) { - if (j->rr) - n++; - else if (j->type == DNS_CACHE_NXDOMAIN) - nxdomain = true; - } - } + log_debug("%s cache hit for %s", + nxdomain ? "NXDOMAIN" : + n > 0 ? "Positive" : "NODATA", + key_str); if (n <= 0) { *ret = NULL; @@ -560,17 +569,13 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { if (!answer) return -ENOMEM; - for (i = 0; i < q->n_keys; i++) { - DnsCacheItem *j; + LIST_FOREACH(by_key, j, first) { + if (!j->rr) + continue; - j = hashmap_get(c->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, j) { - if (j->rr) { - r = dns_answer_add(answer, j->rr); - if (r < 0) - return r; - } - } + r = dns_answer_add(answer, j->rr, 0); + if (r < 0) + return r; } *ret = answer; @@ -615,3 +620,54 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ /* There's a conflict */ return 1; } + +void dns_cache_dump(DnsCache *cache, FILE *f) { + Iterator iterator; + DnsCacheItem *i; + int r; + + if (!cache) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, cache->by_key, iterator) { + DnsCacheItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + fputc('\t', f); + + if (j->rr) { + r = dns_resource_record_to_string(j->rr, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputc('\n', f); + } else { + r = dns_resource_key_to_string(j->key, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputs(" -- ", f); + fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); + fputc('\n', f); + } + } + } +} + +bool dns_cache_is_empty(DnsCache *cache) { + if (!cache) + return true; + + return hashmap_isempty(cache->by_key); +} diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 8a9b3d459d..1225e58de4 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -40,6 +40,9 @@ void dns_cache_flush(DnsCache *c); void dns_cache_prune(DnsCache *c); int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); -int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **answer); +int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer); int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); + +void dns_cache_dump(DnsCache *cache, FILE *f); +bool dns_cache_is_empty(DnsCache *cache); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 35ad899544..bebd1ee4a6 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -388,14 +388,21 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start return 0; } -int dns_packet_append_name(DnsPacket *p, const char *name, - bool allow_compression, size_t *start) { +int dns_packet_append_name( + DnsPacket *p, + const char *name, + bool allow_compression, + size_t *start) { + size_t saved_size; int r; assert(p); assert(name); + if (p->refuse_compression) + allow_compression = false; + saved_size = p->size; while (*name) { @@ -1053,8 +1060,12 @@ fail: return r; } -int dns_packet_read_name(DnsPacket *p, char **_ret, - bool allow_compression, size_t *start) { +int dns_packet_read_name( + DnsPacket *p, + char **_ret, + bool allow_compression, + size_t *start) { + size_t saved_rindex, after_rindex = 0, jump_barrier; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; @@ -1064,6 +1075,9 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, assert(p); assert(_ret); + if (p->refuse_compression) + allow_compression = false; + saved_rindex = p->rindex; jump_barrier = p->rindex; @@ -1795,7 +1809,7 @@ int dns_packet_extract(DnsPacket *p) { if (r < 0) goto finish; - r = dns_answer_add(answer, rr); + r = dns_answer_add(answer, rr, p->ifindex); if (r < 0) goto finish; } diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 58559c85df..5628e579ad 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -86,6 +86,7 @@ struct DnsPacket { uint32_t ttl; bool extracted; + bool refuse_compression; }; static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { @@ -120,15 +121,15 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { #define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount) #define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \ - (((uint16_t) !!qr << 15) | \ - ((uint16_t) (opcode & 15) << 11) | \ - ((uint16_t) !!aa << 10) | \ - ((uint16_t) !!tc << 9) | \ - ((uint16_t) !!rd << 8) | \ - ((uint16_t) !!ra << 7) | \ - ((uint16_t) !!ad << 5) | \ - ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rcode & 15))) + (((uint16_t) !!(qr) << 15) | \ + ((uint16_t) ((opcode) & 15) << 11) | \ + ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \ + ((uint16_t) !!(tc) << 9) | \ + ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \ + ((uint16_t) !!(ra) << 7) | \ + ((uint16_t) !!(ad) << 5) | \ + ((uint16_t) !!(cd) << 4) | \ + ((uint16_t) ((rcode) & 15))) static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { return @@ -238,11 +239,16 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) /* Converts a protocol + family into a flags field as used in queries */ - if (protocol == DNS_PROTOCOL_DNS) + switch (protocol) { + case DNS_PROTOCOL_DNS: return SD_RESOLVED_DNS; - if (protocol == DNS_PROTOCOL_LLMNR) + case DNS_PROTOCOL_LLMNR: return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4; + default: + break; + } + return 0; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 3ada442006..c0b4c8ba81 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -21,6 +21,7 @@ #include "hostname-util.h" #include "dns-domain.h" +#include "local-addresses.h" #include "resolved-dns-query.h" @@ -137,31 +138,20 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { } static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; DnsTransaction *t; int r; assert(q); assert(s); + assert(key); r = set_ensure_allocated(&q->transactions, NULL); if (r < 0) return r; - if (key) { - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } else - question = dns_question_ref(q->question); - - t = dns_scope_find_transaction(s, question, true); + t = dns_scope_find_transaction(s, key, true); if (!t) { - r = dns_transaction_new(&t, s, question); + r = dns_transaction_new(&t, s, key); if (r < 0) return r; } @@ -188,27 +178,18 @@ gc: } static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { + unsigned i; int r; assert(q); assert(s); - if (s->protocol == DNS_PROTOCOL_MDNS) { - r = dns_query_add_transaction(q, s, NULL); + /* Create one transaction per question key */ + + for (i = 0; i < q->question->n_keys; i++) { + r = dns_query_add_transaction(q, s, q->question->keys[i]); if (r < 0) return r; - } else { - unsigned i; - - /* On DNS and LLMNR we can only send a single - * question per datagram, hence issue multiple - * transactions. */ - - for (i = 0; i < q->question->n_keys; i++) { - r = dns_query_add_transaction(q, s, q->question->keys[i]); - if (r < 0) - return r; - } } return 0; @@ -216,9 +197,10 @@ static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { static int SYNTHESIZE_IFINDEX(int ifindex) { - /* When the caller asked for resolving on a specific interface, - * we synthesize the answer for that interface. However, if - * nothing specific was claimed, we synthesize the answer for + /* When the caller asked for resolving on a specific + * interface, we synthesize the answer for that + * interface. However, if nothing specific was claimed and we + * only return localhost RRs, we synthesize the answer for * localhost. */ if (ifindex > 0) @@ -261,7 +243,289 @@ static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) { return DNS_PROTOCOL_DNS; } -static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { +static int dns_type_to_af(uint16_t t) { + switch (t) { + + case DNS_TYPE_A: + return AF_INET; + + case DNS_TYPE_AAAA: + return AF_INET6; + + case DNS_TYPE_ANY: + return AF_UNSPEC; + + default: + return -EINVAL; + } +} + +static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + int r; + + assert(q); + assert(key); + assert(answer); + + r = dns_answer_reserve(answer, 2); + if (r < 0) + return r; + + if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key)); + if (!rr) + return -ENOMEM; + + rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); + + r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key)); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = in6addr_loopback; + + r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from); + if (!rr) + return -ENOMEM; + + rr->ptr.name = strdup(to); + if (!rr->ptr.name) + return -ENOMEM; + + return dns_answer_add(*answer, rr, ifindex); +} + +static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + int r; + + assert(q); + assert(key); + assert(answer); + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) { + r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_rr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + r = dns_answer_reserve(answer, n_addresses); + if (r < 0) + return r; + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_ptr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses, + int af, const union in_addr_union *match) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + if (af != AF_UNSPEC) { + + if (addresses[j].family != af) + continue; + + if (match && !in_addr_equal(af, match, &addresses[j].address)) + continue; + } + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex); + if (r < 0) + return r; + } + + return 0; +} + +static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(q); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + if (n == 0) { + struct local_address buffer[2]; + + /* If we have no local addresses then use ::1 + * and 127.0.0.2 as local ones. */ + + if (af == AF_INET || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET, + .ifindex = SYNTHESIZE_IFINDEX(q->ifindex), + .address.in.s_addr = htobe32(0x7F000002), + }; + + if (af == AF_INET6 || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET6, + .ifindex = SYNTHESIZE_IFINDEX(q->ifindex), + .address.in6 = in6addr_loopback, + }; + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n); + } + } + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); +} + +static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n, r; + + assert(q); + assert(address); + assert(answer); + + if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { + + /* Always map the IPv4 address 127.0.0.2 to the local + * hostname, in addition to "localhost": */ + + r = dns_answer_reserve(answer, 3); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex)); + if (r < 0) + return r; + + return 0; + } + + n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address); + if (r < 0) + return r; + + return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address); +} + +static int synthesize_gateway_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(q); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + } + + return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); +} + +static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n; + + assert(q); + assert(address); + assert(answer); + + n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses); + if (n < 0) + return n; + + return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address); +} + +static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; unsigned i; int r; @@ -276,11 +540,12 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) DNS_TRANSACTION_NO_SERVERS, DNS_TRANSACTION_TIMEOUT, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED)) - return; + return 0; for (i = 0; i < q->question->n_keys; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + union in_addr_union address; const char *name; + int af; if (q->question->keys[i]->class != DNS_CLASS_IN && q->question->keys[i]->class != DNS_CLASS_ANY) @@ -290,78 +555,55 @@ static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) if (is_localhost(name)) { - switch (q->question->keys[i]->type) { - - case DNS_TYPE_A: - case DNS_TYPE_ANY: - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); - if (!rr) { - log_oom(); - return; - } + r = synthesize_localhost_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); - rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); - break; + } else if (manager_is_own_hostname(q->manager, name)) { - case DNS_TYPE_AAAA: - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); - if (!rr) { - log_oom(); - return; - } + r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize system hostname RRs: %m"); - rr->aaaa.in6_addr = in6addr_loopback; - break; - } + } else if (is_gateway_hostname(name)) { - } else if (IN_SET(q->question->keys[i]->type, DNS_TYPE_PTR, DNS_TYPE_ANY) && - (dns_name_endswith(name, "127.in-addr.arpa") > 0 || - dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)) { + r = synthesize_gateway_rr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize gateway RRs: %m"); - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, name); - if (!rr) { - log_oom(); - return; - } + } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) || + dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) { - rr->ptr.name = strdup("localhost"); - if (!rr->ptr.name) { - log_oom(); - return; - } - } + r = synthesize_localhost_ptr(q, q->question->keys[i], &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); - if (!rr) - continue; + } else if (dns_name_address(name, &af, &address) > 0) { - if (!answer) { - answer = dns_answer_new(q->question->n_keys); - if (!answer) { - log_oom(); - return; - } - } + r = synthesize_system_hostname_ptr(q, af, &address, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m"); - r = dns_answer_add(answer, rr); - if (r < 0) { - log_error_errno(r, "Failed to add synthetic RR to answer: %m"); - return; + r = synthesize_gateway_ptr(q, af, &address, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m"); } } if (!answer) - return; + return 0; dns_answer_unref(q->answer); q->answer = answer; answer = NULL; - q->answer_ifindex = SYNTHESIZE_IFINDEX(q->ifindex); q->answer_family = SYNTHESIZE_FAMILY(q->flags); q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); q->answer_rcode = DNS_RCODE_SUCCESS; *state = DNS_TRANSACTION_SUCCESS; + + return 1; } int dns_query_go(DnsQuery *q) { @@ -409,12 +651,8 @@ int dns_query_go(DnsQuery *q) { DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; dns_query_synthesize_reply(q, &state); - if (state != DNS_TRANSACTION_NO_SERVERS) { - dns_query_complete(q, state); - return 1; - } - - return -ESRCH; + dns_query_complete(q, state); + return 1; } r = dns_query_add_transaction_split(q, first); @@ -437,7 +675,6 @@ int dns_query_go(DnsQuery *q) { } q->answer = dns_answer_unref(q->answer); - q->answer_ifindex = 0; q->answer_rcode = 0; q->answer_family = AF_UNSPEC; q->answer_protocol = _DNS_PROTOCOL_INVALID; @@ -584,7 +821,6 @@ void dns_query_ready(DnsQuery *q) { if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) { q->answer = dns_answer_ref(answer); q->answer_rcode = rcode; - q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0; q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID; q->answer_family = scope ? scope->family : AF_UNSPEC; } diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 5a319f0a62..93d49301fa 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -45,7 +45,6 @@ struct DnsQuery { /* Discovered data */ DnsAnswer *answer; - int answer_ifindex; int answer_family; DnsProtocol answer_protocol; int answer_rcode; diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 0efe740d1a..c94928d725 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -68,9 +68,11 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { unsigned i; int r; - assert(q); assert(key); + if (!q) + return -ENOSPC; + for (i = 0; i < q->n_keys; i++) { r = dns_resource_key_equal(q->keys[i], key); if (r < 0) @@ -90,9 +92,11 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { unsigned i; int r; - assert(q); assert(rr); + if (!q) + return 0; + for (i = 0; i < q->n_keys; i++) { r = dns_resource_key_match_rr(q->keys[i], rr); if (r != 0) @@ -106,9 +110,11 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { unsigned i; int r; - assert(q); assert(rr); + if (!q) + return 0; + for (i = 0; i < q->n_keys; i++) { r = dns_resource_key_match_cname(q->keys[i], rr); if (r != 0) @@ -123,7 +129,8 @@ int dns_question_is_valid(DnsQuestion *q) { unsigned i; int r; - assert(q); + if (!q) + return 0; if (q->n_keys <= 0) return 0; @@ -151,16 +158,19 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) { unsigned j; int r; - assert(q); - assert(other); - /* Checks if all keys in "other" are also contained in "q" */ + if (!other) + return 1; + for (j = 0; j < other->n_keys; j++) { DnsResourceKey *b = other->keys[j]; bool found = false; unsigned i; + if (!q) + return 0; + for (i = 0; i < q->n_keys; i++) { DnsResourceKey *a = q->keys[i]; @@ -192,9 +202,11 @@ int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) { unsigned j; int r; - assert(a); assert(k); + if (!a) + return 0; + for (j = 0; j < a->n_keys; j++) { r = dns_resource_key_equal(a->keys[j], k); if (r != 0) @@ -208,8 +220,10 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { unsigned j; int r; - assert(a); - assert(b); + if (!a) + return !b || b->n_keys == 0; + if (!b) + return a->n_keys == 0; /* Checks if all keys in a are also contained b, and vice versa */ @@ -234,10 +248,19 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion ** unsigned i; int r; - assert(q); assert(name); assert(ret); + if (!q) { + n = dns_question_new(0); + if (!n) + return -ENOMEM; + + *ret = n; + n = 0; + return 0; + } + for (i = 0; i < q->n_keys; i++) { r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); if (r < 0) @@ -277,38 +300,3 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion ** return 1; } - -int dns_question_endswith(DnsQuestion *q, const char *suffix) { - unsigned i; - - assert(q); - assert(suffix); - - for (i = 0; i < q->n_keys; i++) { - int k; - - k = dns_name_endswith(DNS_RESOURCE_KEY_NAME(q->keys[i]), suffix); - if (k <= 0) - return k; - } - - return 1; -} - -int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address) { - unsigned i; - - assert(q); - assert(family); - assert(address); - - for (i = 0; i < q->n_keys; i++) { - int k; - - k = dns_name_address(DNS_RESOURCE_KEY_NAME(q->keys[i]), family, address); - if (k != 0) - return k; - } - - return 0; -} diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index fc98677798..77de0c7a2c 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -48,7 +48,4 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret); -int dns_question_endswith(DnsQuestion *q, const char *suffix); -int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address); - DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index ad7ca26cfe..f31644eebc 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -350,6 +350,36 @@ int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const u return 0; } +int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) { + DnsResourceRecord *rr; + + assert(ret); + assert(address); + assert(family); + + if (family == AF_INET) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); + if (!rr) + return -ENOMEM; + + rr->a.in_addr = address->in; + + } else if (family == AF_INET6) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = address->in6; + } else + return -EAFNOSUPPORT; + + *ret = rr; + + return 0; +} + int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { int r; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 0f40f3ceef..8986a298af 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -191,6 +191,7 @@ DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, c DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr); int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); +int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 57d9071dfc..9e6f595a1b 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -78,8 +78,7 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_scope_llmnr_membership(s, false); - while ((t = s->transactions)) { - + while ((t = hashmap_steal_first(s->transactions))) { /* Abort the transaction, but make sure it is not * freed while we still look at it */ @@ -90,6 +89,8 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_transaction_free(t); } + hashmap_free(s->transactions); + while ((rr = ordered_hashmap_steal_first(s->conflict_queue))) dns_resource_record_unref(rr); @@ -165,7 +166,8 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { } else mtu = manager_find_mtu(s->manager); - if (s->protocol == DNS_PROTOCOL_DNS) { + switch (s->protocol) { + case DNS_PROTOCOL_DNS: if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; @@ -179,8 +181,9 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { if (r < 0) return r; - } else if (s->protocol == DNS_PROTOCOL_LLMNR) { + break; + case DNS_PROTOCOL_LLMNR: if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; @@ -204,8 +207,12 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); if (r < 0) return r; - } else + + break; + + default: return -EAFNOSUPPORT; + } return 1; } @@ -328,11 +335,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (dns_name_root(domain) != 0) return DNS_SCOPE_NO; - if (is_localhost(domain)) - return DNS_SCOPE_NO; - - /* Never resolve any loopback IP address via DNS, LLMNR or mDNS */ - if (dns_name_endswith(domain, "127.in-addr.arpa") > 0 || + /* Never resolve any loopback hostname or IP address via DNS, + * LLMNR or mDNS. Instead, always rely on synthesized RRs for + * these. */ + if (is_localhost(domain) || + dns_name_endswith(domain, "127.in-addr.arpa") > 0 || dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; @@ -340,35 +347,38 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (dns_name_endswith(domain, *i) > 0) return DNS_SCOPE_YES; - if (s->protocol == DNS_PROTOCOL_DNS) { + switch (s->protocol) { + case DNS_PROTOCOL_DNS: if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && dns_name_single_label(domain) == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - if (s->protocol == DNS_PROTOCOL_MDNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 || - (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0)) + case DNS_PROTOCOL_MDNS: + if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || + (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ + dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */ + manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */ return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - if (s->protocol == DNS_PROTOCOL_LLMNR) { - if (dns_name_endswith(domain, "in-addr.arpa") > 0 || - dns_name_endswith(domain, "ip6.arpa") > 0 || - (dns_name_single_label(domain) > 0 && - dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ + case DNS_PROTOCOL_LLMNR: + if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || + (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */ + !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ + manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - assert_not_reached("Unknown scope protocol"); + default: + assert_not_reached("Unknown scope protocol"); + } } int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { @@ -490,7 +500,7 @@ static int dns_scope_make_reply_packet( if (answer) { for (i = 0; i < answer->n_rrs; i++) { - r = dns_packet_append_rr(p, answer->rrs[i], NULL); + r = dns_packet_append_rr(p, answer->items[i].rr, NULL); if (r < 0) return r; } @@ -500,7 +510,7 @@ static int dns_scope_make_reply_packet( if (soa) { for (i = 0; i < soa->n_rrs; i++) { - r = dns_packet_append_rr(p, soa->rrs[i], NULL); + r = dns_packet_append_rr(p, soa->items[i].rr, NULL); if (r < 0) return r; } @@ -525,7 +535,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { dns_zone_verify_conflicts(&s->zone, p->question->keys[n]); if (p->answer) for (n = 0; n < p->answer->n_rrs; n++) - dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key); + dns_zone_verify_conflicts(&s->zone, p->answer->items[n].rr->key); } void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { @@ -614,30 +624,26 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { } } -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok) { +DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok) { DnsTransaction *t; assert(scope); - assert(question); - - /* Try to find an ongoing transaction that is a equal or a - * superset of the specified question */ - - LIST_FOREACH(transactions_by_scope, t, scope->transactions) { + assert(key); - /* Refuse reusing transactions that completed based on - * cached data instead of a real packet, if that's - * requested. */ - if (!cache_ok && - IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && - !t->received) - continue; + /* Try to find an ongoing transaction that is a equal to the + * specified question */ + t = hashmap_get(scope->transactions, key); + if (!t) + return NULL; - if (dns_question_is_superset(t->question, question) > 0) - return t; - } + /* Refuse reusing transactions that completed based on cached + * data instead of a real packet, if that's requested. */ + if (!cache_ok && + IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && + !t->received) + return NULL; - return NULL; + return t; } static int dns_scope_make_conflict_packet( @@ -793,16 +799,48 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { /* Check for conflicts against the local zone. If we * found one, we won't check any further */ - r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]); + r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr); if (r != 0) continue; /* Check for conflicts against the local cache. If so, * send out an advisory query, to inform everybody */ - r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender); + r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender); if (r <= 0) continue; - dns_scope_notify_conflict(scope, p->answer->rrs[i]); + dns_scope_notify_conflict(scope, p->answer->items[i].rr); + } +} + +void dns_scope_dump(DnsScope *s, FILE *f) { + assert(s); + + if (!f) + f = stdout; + + fputs("[Scope protocol=", f); + fputs(dns_protocol_to_string(s->protocol), f); + + if (s->link) { + fputs(" interface=", f); + fputs(s->link->name, f); + } + + if (s->family != AF_UNSPEC) { + fputs(" family=", f); + fputs(af_to_name(s->family), f); + } + + fputs("]\n", f); + + if (!dns_zone_is_empty(&s->zone)) { + fputs("ZONE:\n", f); + dns_zone_dump(&s->zone, f); + } + + if (!dns_cache_is_empty(&s->cache)) { + fputs("CACHE:\n", f); + dns_cache_dump(&s->cache, f); } } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index b2dac86b44..b75f212897 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -60,7 +60,7 @@ struct DnsScope { usec_t resend_timeout; usec_t max_rtt; - LIST_HEAD(DnsTransaction, transactions); + Hashmap *transactions; LIST_FIELDS(DnsScope, scopes); }; @@ -85,7 +85,9 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b); void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok); +DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok); int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); + +void dns_scope_dump(DnsScope *s, FILE *f); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 2d9d1a47ee..8092bb514d 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -24,6 +24,7 @@ #include "resolved-llmnr.h" #include "resolved-dns-transaction.h" #include "random-util.h" +#include "dns-domain.h" DnsTransaction* dns_transaction_free(DnsTransaction *t) { DnsQuery *q; @@ -34,24 +35,25 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { sd_event_source_unref(t->timeout_event_source); - dns_question_unref(t->question); dns_packet_unref(t->sent); dns_packet_unref(t->received); dns_answer_unref(t->cached); - sd_event_source_unref(t->dns_event_source); - safe_close(t->dns_fd); + sd_event_source_unref(t->dns_udp_event_source); + safe_close(t->dns_udp_fd); dns_server_unref(t->server); dns_stream_free(t->stream); if (t->scope) { - LIST_REMOVE(transactions_by_scope, t->scope->transactions, t); + hashmap_remove(t->scope->transactions, t->key); if (t->id != 0) hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id)); } + dns_resource_key_unref(t->key); + while ((q = set_steal_first(t->queries))) set_remove(q->transactions, t); set_free(t->queries); @@ -76,26 +78,30 @@ void dns_transaction_gc(DnsTransaction *t) { dns_transaction_free(t); } -int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { +int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) { _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL; int r; assert(ret); assert(s); - assert(q); + assert(key); r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL); if (r < 0) return r; + r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops); + if (r < 0) + return r; + t = new0(DnsTransaction, 1); if (!t) return -ENOMEM; - t->dns_fd = -1; - - t->question = dns_question_ref(q); + t->dns_udp_fd = -1; + t->key = dns_resource_key_ref(key); + /* Find a fresh, unused transaction id */ do random_bytes(&t->id, sizeof(t->id)); while (t->id == 0 || @@ -107,7 +113,12 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { return r; } - LIST_PREPEND(transactions_by_scope, s->transactions, t); + r = hashmap_put(s->transactions, t->key, t); + if (r < 0) { + hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id)); + return r; + } + t->scope = s; if (ret) @@ -175,9 +186,6 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { assert(t); assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) - return; - /* Note that this call might invalidate the query. Callers * should hence not attempt to access the query or transaction * after calling this function. */ @@ -252,11 +260,13 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { if (t->stream) return 0; - if (t->scope->protocol == DNS_PROTOCOL_DNS) + switch (t->scope->protocol) { + case DNS_PROTOCOL_DNS: fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server); - else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { + break; - /* When we already received a query to this (but it was truncated), send to its sender address */ + case DNS_PROTOCOL_LLMNR: + /* When we already received a reply to this (but it was truncated), send to its sender address */ if (t->received) fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL); else { @@ -266,16 +276,23 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { /* Otherwise, try to talk to the owner of a * the IP address, in case this is a reverse * PTR lookup */ - r = dns_question_extract_reverse_address(t->question, &family, &address); + + r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address); if (r < 0) return r; if (r == 0) return -EINVAL; + if (family != t->scope->family) + return -ESRCH; fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL); } - } else + + break; + + default: return -EAFNOSUPPORT; + } if (fd < 0) return fd; @@ -292,7 +309,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { return r; } - dns_server_unref(t->server); t->server = dns_server_ref(server); t->received = dns_packet_unref(t->received); @@ -312,8 +328,8 @@ static void dns_transaction_next_dns_server(DnsTransaction *t) { assert(t); t->server = dns_server_unref(t->server); - t->dns_event_source = sd_event_source_unref(t->dns_event_source); - t->dns_fd = safe_close(t->dns_fd); + t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source); + t->dns_udp_fd = safe_close(t->dns_udp_fd); dns_scope_next_dns_server(t->scope); } @@ -332,7 +348,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { * should hence not attempt to access the query or transaction * after calling this function. */ - if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { + switch (t->scope->protocol) { + case DNS_PROTOCOL_LLMNR: assert(t->scope->link); /* For LLMNR we will not accept any packets from other @@ -351,6 +368,14 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { dns_transaction_tentative(t, p); return; } + + break; + + case DNS_PROTOCOL_DNS: + break; + + default: + assert_not_reached("Invalid DNS protocol."); } if (t->received != p) { @@ -387,7 +412,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { break; default: - assert_not_reached("Invalid DNS protocol."); + break; } if (DNS_PACKET_TC(p)) { @@ -427,8 +452,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* Only consider responses with equivalent query section to the request */ - r = dns_question_is_equal(p->question, t->question); - if (r <= 0) { + if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) { dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); return; } @@ -476,16 +500,16 @@ static int dns_transaction_emit(DnsTransaction *t) { if (fd < 0) return fd; - r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t); + r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t); if (r < 0) return r; - t->dns_fd = fd; + t->dns_udp_fd = fd; fd = -1; t->server = dns_server_ref(server); } - r = dns_scope_emit(t->scope, t->dns_fd, t->sent); + r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent); if (r < 0) return r; @@ -517,7 +541,6 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat static int dns_transaction_make_packet(DnsTransaction *t) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - unsigned n, added = 0; int r; assert(t); @@ -529,24 +552,17 @@ static int dns_transaction_make_packet(DnsTransaction *t) { if (r < 0) return r; - for (n = 0; n < t->question->n_keys; n++) { - r = dns_scope_good_key(t->scope, t->question->keys[n]); - if (r < 0) - return r; - if (r == 0) - continue; - - r = dns_packet_append_key(p, t->question->keys[n], NULL); - if (r < 0) - return r; - - added++; - } - - if (added <= 0) + r = dns_scope_good_key(t->scope, t->key); + if (r < 0) + return r; + if (r == 0) return -EDOM; - DNS_PACKET_HEADER(p)->qdcount = htobe16(added); + r = dns_packet_append_key(p, t->key, NULL); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->qdcount = htobe16(1); DNS_PACKET_HEADER(p)->id = t->id; t->sent = p; @@ -620,7 +636,7 @@ int dns_transaction_go(DnsTransaction *t) { /* Let's then prune all outdated entries */ dns_cache_prune(&t->scope->cache); - r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached); + r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached); if (r < 0) return r; if (r > 0) { @@ -673,8 +689,8 @@ int dns_transaction_go(DnsTransaction *t) { return r; if (t->scope->protocol == DNS_PROTOCOL_LLMNR && - (dns_question_endswith(t->question, "in-addr.arpa") > 0 || - dns_question_endswith(t->question, "ip6.arpa") > 0)) { + (dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 || + dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) { /* RFC 4795, Section 2.4. says reverse lookups shall * always be made via TCP on LLMNR */ diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index d8a5647609..acf6a6f651 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -47,7 +47,7 @@ enum DnsTransactionState { struct DnsTransaction { DnsScope *scope; - DnsQuestion *question; + DnsResourceKey *key; DnsTransactionState state; uint16_t id; @@ -62,10 +62,10 @@ struct DnsTransaction { sd_event_source *timeout_event_source; unsigned n_attempts; - int dns_fd; - sd_event_source *dns_event_source; + int dns_udp_fd; + sd_event_source *dns_udp_event_source; - /* the active server */ + /* The active server */ DnsServer *server; /* TCP connection logic, if we need it */ @@ -84,7 +84,7 @@ struct DnsTransaction { LIST_FIELDS(DnsTransaction, transactions_by_scope); }; -int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q); +int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key); DnsTransaction* dns_transaction_free(DnsTransaction *t); void dns_transaction_gc(DnsTransaction *t); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 32d771a954..674bb6af28 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -166,7 +166,6 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { static int dns_zone_item_probe_start(DnsZoneItem *i) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; DnsTransaction *t; int r; @@ -179,17 +178,9 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (!key) return -ENOMEM; - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - - t = dns_scope_find_transaction(i->scope, question, false); + t = dns_scope_find_transaction(i->scope, key, false); if (!t) { - r = dns_transaction_new(&t, i->scope, question); + r = dns_transaction_new(&t, i->scope, key); if (r < 0) return r; } @@ -217,7 +208,6 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { } dns_zone_item_ready(i); - return 0; gc: @@ -422,7 +412,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe if (k < 0) return k; if (k > 0) { - r = dns_answer_add(answer, j->rr); + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; @@ -448,7 +438,7 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe if (j->state != DNS_ZONE_ITEM_PROBING) tentative = false; - r = dns_answer_add(answer, j->rr); + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; } @@ -505,7 +495,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) { i->state = DNS_ZONE_ITEM_WITHDRAWN; /* Maybe change the hostname */ - if (dns_name_equal(i->scope->manager->hostname, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) + if (manager_is_own_hostname(i->scope->manager, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) manager_next_hostname(i->scope->manager); } @@ -646,3 +636,40 @@ void dns_zone_verify_all(DnsZone *zone) { dns_zone_item_verify(j); } } + +void dns_zone_dump(DnsZone *zone, FILE *f) { + Iterator iterator; + DnsZoneItem *i; + int r; + + if (!zone) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, zone->by_key, iterator) { + DnsZoneItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + r = dns_resource_record_to_string(j->rr, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputc('\t', f); + fputs(t, f); + fputc('\n', f); + } + } +} + +bool dns_zone_is_empty(DnsZone *zone) { + if (!zone) + return true; + + return hashmap_isempty(zone->by_key); +} diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 71851265c6..495d17cdb1 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -78,3 +78,6 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key); void dns_zone_verify_all(DnsZone *zone); void dns_zone_item_probe_stop(DnsZoneItem *i); + +void dns_zone_dump(DnsZone *zone, FILE *f); +bool dns_zone_is_empty(DnsZone *zone); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index d66b3a88fc..47f461a37d 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -419,16 +419,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->link->llmnr_support == SUPPORT_YES && a->link->manager->llmnr_support == SUPPORT_YES) { - if (!a->link->manager->host_ipv4_key) { - a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname); - if (!a->link->manager->host_ipv4_key) { + if (!a->link->manager->llmnr_host_ipv4_key) { + a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname); + if (!a->link->manager->llmnr_host_ipv4_key) { r = -ENOMEM; goto fail; } } if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key); + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key); if (!a->llmnr_address_rr) { r = -ENOMEM; goto fail; @@ -439,7 +439,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { } if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); if (r < 0) goto fail; @@ -476,16 +476,16 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->link->llmnr_support == SUPPORT_YES && a->link->manager->llmnr_support == SUPPORT_YES) { - if (!a->link->manager->host_ipv6_key) { - a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname); - if (!a->link->manager->host_ipv6_key) { + if (!a->link->manager->llmnr_host_ipv6_key) { + a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname); + if (!a->link->manager->llmnr_host_ipv6_key) { r = -ENOMEM; goto fail; } } if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key); + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key); if (!a->llmnr_address_rr) { r = -ENOMEM; goto fail; @@ -496,7 +496,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { } if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); if (r < 0) goto fail; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 645f2a824c..13852192c4 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -310,51 +310,84 @@ static int manager_network_monitor_listen(Manager *m) { return 0; } -static int determine_hostname(char **ret) { +static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL; - int r; + char label[DNS_LABEL_MAX]; + const char *p; + int r, k; - assert(ret); + assert(llmnr_hostname); + assert(mdns_hostname); + + /* Extract and normalize the first label of the locally + * configured hostname, and check it's not "localhost". */ h = gethostname_malloc(); if (!h) return log_oom(); - if (!utf8_is_valid(h)) { + p = h; + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return log_error_errno(r, "Failed to unescape host name: %m"); + if (r == 0) { + log_error("Couldn't find a single label in hosntame."); + return -EINVAL; + } + + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + return log_error_errno(k, "Failed to undo IDNA: %m"); + if (k > 0) + r = k; + + if (!utf8_is_valid(label)) { log_error("System hostname is not UTF-8 clean."); return -EINVAL; } - r = dns_name_normalize(h, &n); - if (r < 0) { - log_error("System hostname '%s' cannot be normalized.", h); - return r; + r = dns_label_escape(label, r, &n); + if (r < 0) + return log_error_errno(r, "Failed to escape host name: %m"); + + if (is_localhost(n)) { + log_debug("System hostname is 'localhost', ignoring."); + return -EINVAL; } - *ret = n; + r = dns_name_concat(n, "local", mdns_hostname); + if (r < 0) + return log_error_errno(r, "Failed to determine mDNS hostname: %m"); + + *llmnr_hostname = n; n = NULL; return 0; } static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *h = NULL; + _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; Manager *m = userdata; int r; assert(m); - r = determine_hostname(&h); + r = determine_hostname(&llmnr_hostname, &mdns_hostname); if (r < 0) return 0; /* ignore invalid hostnames */ - if (streq(h, m->hostname)) + if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) return 0; - log_info("System hostname changed to '%s'.", h); - free(m->hostname); - m->hostname = h; - h = NULL; + log_info("System hostname changed to '%s'.", llmnr_hostname); + + free(m->llmnr_hostname); + free(m->mdns_hostname); + + m->llmnr_hostname = llmnr_hostname; + m->mdns_hostname = mdns_hostname; + + llmnr_hostname = mdns_hostname = NULL; manager_refresh_rrs(m); @@ -381,15 +414,44 @@ static int manager_watch_hostname(Manager *m) { return log_error_errno(r, "Failed to add hostname event source: %m"); } - r = determine_hostname(&m->hostname); + r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname); if (r < 0) { log_info("Defaulting to hostname 'linux'."); - m->hostname = strdup("linux"); - if (!m->hostname) + m->llmnr_hostname = strdup("linux"); + if (!m->llmnr_hostname) + return log_oom(); + + m->mdns_hostname = strdup("linux.local"); + if (!m->mdns_hostname) return log_oom(); } else - log_info("Using system hostname '%s'.", m->hostname); + log_info("Using system hostname '%s'.", m->llmnr_hostname); + + return 0; +} + +static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + size_t size = 0; + DnsScope *scope; + + assert(s); + assert(si); + assert(m); + + f = open_memstream(&buffer, &size); + if (!f) + return log_oom(); + + LIST_FOREACH(scopes, scope, m->dns_scopes) + dns_scope_dump(scope, f); + + if (fflush_and_check(f) < 0) + return log_oom(); + log_dump(LOG_INFO, buffer); return 0; } @@ -443,6 +505,8 @@ int manager_new(Manager **ret) { if (r < 0) return r; + (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); + *ret = m; m = NULL; @@ -490,14 +554,17 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->bus_retry_event_source); sd_bus_unref(m->bus); + sd_event_source_unref(m->sigusr1_event_source); + sd_event_unref(m->event); - dns_resource_key_unref(m->host_ipv4_key); - dns_resource_key_unref(m->host_ipv6_key); + dns_resource_key_unref(m->llmnr_host_ipv4_key); + dns_resource_key_unref(m->llmnr_host_ipv6_key); - safe_close(m->hostname_fd); sd_event_source_unref(m->hostname_event_source); - free(m->hostname); + safe_close(m->hostname_fd); + free(m->llmnr_hostname); + free(m->mdns_hostname); free(m); @@ -1229,8 +1296,8 @@ void manager_refresh_rrs(Manager *m) { assert(m); - m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key); - m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key); + m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); + m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); HASHMAP_FOREACH(l, m->links, i) { link_add_rrs(l, true); @@ -1241,14 +1308,15 @@ void manager_refresh_rrs(Manager *m) { int manager_next_hostname(Manager *m) { const char *p; uint64_t u, a; - char *h; + char *h, *k; + int r; assert(m); - p = strchr(m->hostname, 0); + p = strchr(m->llmnr_hostname, 0); assert(p); - while (p > m->hostname) { + while (p > m->llmnr_hostname) { if (!strchr("0123456789", p[-1])) break; @@ -1268,13 +1336,22 @@ int manager_next_hostname(Manager *m) { random_bytes(&a, sizeof(a)); u += 1 + a % 10; - if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0) + if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0) return -ENOMEM; - log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h); + r = dns_name_concat(h, "local", &k); + if (r < 0) { + free(h); + return r; + } + + log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h); - free(m->hostname); - m->hostname = h; + free(m->llmnr_hostname); + m->llmnr_hostname = h; + + free(m->mdns_hostname); + m->mdns_hostname = k; manager_refresh_rrs(m); @@ -1356,6 +1433,24 @@ void manager_flush_dns_servers(Manager *m, DnsServerType t) { } } +int manager_is_own_hostname(Manager *m, const char *name) { + int r; + + assert(m); + assert(name); + + if (m->llmnr_hostname) { + r = dns_name_equal(name, m->llmnr_hostname); + if (r != 0) + return r; + } + + if (m->mdns_hostname) + return dns_name_equal(name, m->mdns_hostname); + + return 0; +} + static const char* const support_table[_SUPPORT_MAX] = { [SUPPORT_NO] = "no", [SUPPORT_YES] = "yes", diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 53b5acb33c..fe7fe99505 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -91,9 +91,10 @@ struct Manager { sd_event_source *bus_retry_event_source; /* The hostname we publish on LLMNR and mDNS */ - char *hostname; - DnsResourceKey *host_ipv4_key; - DnsResourceKey *host_ipv6_key; + char *llmnr_hostname; + char *mdns_hostname; + DnsResourceKey *llmnr_host_ipv4_key; + DnsResourceKey *llmnr_host_ipv6_key; /* Watch the system hostname */ int hostname_fd; @@ -101,6 +102,8 @@ struct Manager { /* Watch for system suspends */ sd_bus_slot *prepare_for_sleep_slot; + + sd_event_source *sigusr1_event_source; }; /* Manager */ @@ -140,5 +143,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define EXTRA_CMSG_SPACE 1024 +int manager_is_own_hostname(Manager *m, const char *name); + const char* support_to_string(Support p) _const_; int support_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 0af5545f8e..32e61af925 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, -1) >= 0); r = manager_new(&m); if (r < 0) { diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 0df0594ea3..1bcb8903f3 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1382,7 +1382,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies")) { + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) { r = parse_boolean(eq); if (r < 0) { @@ -1414,7 +1415,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "t", u); - } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode")) + } else if (STR_IN_SET(field, + "User", "Group", "DevicePolicy", "KillMode", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "StandardInput", "StandardOutput", "StandardError", + "Description", "Slice", "Type")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "DeviceAllow")) { diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 8a0dec1540..6dc04d51e4 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -308,14 +308,14 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif } -int dns_name_normalize(const char *s, char **_ret) { +int dns_name_concat(const char *a, const char *b, char **_ret) { _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; - const char *p = s; + const char *p = a; bool first = true; int r; - assert(s); + assert(a); for (;;) { _cleanup_free_ char *t = NULL; @@ -328,6 +328,14 @@ int dns_name_normalize(const char *s, char **_ret) { if (r == 0) { if (*p != 0) return -EINVAL; + + if (b) { + /* Now continue with the second string, if there is one */ + p = b; + b = NULL; + continue; + } + break; } @@ -341,27 +349,29 @@ int dns_name_normalize(const char *s, char **_ret) { if (r < 0) return r; - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) - return -ENOMEM; + if (_ret) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) + return -ENOMEM; - if (!first) - ret[n++] = '.'; - else - first = false; + if (!first) + ret[n++] = '.'; + else + first = false; + + memcpy(ret + n, t, r); + } - memcpy(ret + n, t, r); n += r; } if (n > DNS_NAME_MAX) return -EINVAL; - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - - ret[n] = 0; - if (_ret) { + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + + ret[n] = 0; *_ret = ret; ret = NULL; } diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index bd50ad3e6d..8e73d9c20f 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -35,9 +35,17 @@ int dns_label_escape(const char *p, size_t l, char **ret); int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -int dns_name_normalize(const char *s, char **_ret); +int dns_name_concat(const char *a, const char *b, char **ret); + +static inline int dns_name_normalize(const char *s, char **ret) { + /* dns_name_concat() normalizes as a side-effect */ + return dns_name_concat(s, NULL, ret); +} + static inline int dns_name_is_valid(const char *s) { int r; + + /* dns_name_normalize() verifies as a side effect */ r = dns_name_normalize(s, NULL); if (r == -EINVAL) return 0; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 60144ab8a6..9a5ffb7a6c 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -34,6 +34,7 @@ #include "formats-util.h" #include "process-util.h" #include "terminal-util.h" +#include "hostname-util.h" /* up to three lines (each up to 100 characters), or 300 characters, whichever is less */ diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 8f66df7718..63f1e4ca6f 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { struct utmpx store = { .ut_type = INIT_PROCESS, .ut_pid = pid, .ut_session = sid, }; + int r; assert(id); @@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line if (line) strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); - return write_entry_both(&store); + r = write_entry_both(&store); + if (r < 0) + return r; + + if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { + store.ut_type = LOGIN_PROCESS; + r = write_entry_both(&store); + if (r < 0) + return r; + } + + if (ut_type == USER_PROCESS) { + store.ut_type = USER_PROCESS; + strncpy(store.ut_user, user, sizeof(store.ut_user)-1); + r = write_entry_both(&store); + if (r < 0) + return r; + } + + return 0; } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 5d26ba6fb1..e0ceb873ac 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp); int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); int utmp_wall( const char *message, @@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) { static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { return 0; } -static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { return 0; } static inline int utmp_wall( diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 1a9dbadbe1..3cb5f61868 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2794,6 +2794,33 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { return -EINVAL; } + if (!strv_isempty(arg_wall)) { + _cleanup_free_ char *m; + + m = strv_join(arg_wall, " "); + if (!m) + return log_oom(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); + + if (r < 0) { + log_warning_errno(r, "Failed to set wall message, ignoring: %s", + bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + } + + r = sd_bus_call_method( bus, "org.freedesktop.login1", @@ -6260,6 +6287,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_PRESET_MODE, ARG_FIRMWARE_SETUP, ARG_NOW, + ARG_MESSAGE, }; static const struct option options[] = { @@ -6304,6 +6332,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, { "now", no_argument, NULL, ARG_NOW }, + { "message", required_argument, NULL, ARG_MESSAGE }, {} }; @@ -6588,6 +6617,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_now = true; break; + case ARG_MESSAGE: + if (strv_extend(&arg_wall, optarg) < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -7356,30 +7390,20 @@ static int halt_main(sd_bus *bus) { if (!m) return log_oom(); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "WallMessage", - &error, - "s", m); - if (r < 0) { - log_warning_errno(r, "Failed to set WallMessage property in logind: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } + r = sd_bus_call_method( + b, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "EnableWallMessages", - &error, - "b", !arg_no_wall); if (r < 0) { - log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s", + log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); } @@ -7537,30 +7561,20 @@ int main(int argc, char*argv[]) { } } - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "WallMessage", - &error, - "s", arg_wall); - if (r < 0) { - log_warning_errno(r, "Failed to set WallMessage property in logind: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } + r = sd_bus_call_method( + b, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "EnableWallMessages", - &error, - "b", !arg_no_wall); if (r < 0) { - log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s", + log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); } diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 716d7678f1..dc3df3bbf7 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -7,7 +7,7 @@ This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -33,6 +33,12 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs); +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 0042722c99..2193eb6f7d 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -251,6 +251,39 @@ static void test_dns_name_reverse(void) { test_dns_name_reverse_one("::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"); } +static void test_dns_name_concat_one(const char *a, const char *b, int r, const char *result) { + _cleanup_free_ char *p = NULL; + + assert_se(dns_name_concat(a, b, &p) == r); + assert_se(streq_ptr(p, result)); +} + +static void test_dns_name_concat(void) { + test_dns_name_concat_one("foo", "bar", 0, "foo.bar"); + test_dns_name_concat_one("foo.foo", "bar.bar", 0, "foo.foo.bar.bar"); + test_dns_name_concat_one("foo", NULL, 0, "foo"); + test_dns_name_concat_one("foo.", "bar.", 0, "foo.bar"); +} + +static void test_dns_name_is_valid_one(const char *s, int ret) { + assert_se(dns_name_is_valid(s) == ret); +} + +static void test_dns_name_is_valid(void) { + test_dns_name_is_valid_one("foo", 1); + test_dns_name_is_valid_one("foo.", 1); + test_dns_name_is_valid_one("Foo", 1); + test_dns_name_is_valid_one("foo.bar", 1); + test_dns_name_is_valid_one("foo.bar.baz", 1); + test_dns_name_is_valid_one("", 1); + test_dns_name_is_valid_one("foo..bar", 0); + test_dns_name_is_valid_one(".foo.bar", 0); + test_dns_name_is_valid_one("foo.bar.", 1); + test_dns_name_is_valid_one("\\zbar", 0); + test_dns_name_is_valid_one("รค", 1); + test_dns_name_is_valid_one("\n", 0); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -263,6 +296,8 @@ int main(int argc, char *argv[]) { test_dns_name_root(); test_dns_name_single_label(); test_dns_name_reverse(); + test_dns_name_concat(); + test_dns_name_is_valid(); return 0; } diff --git a/src/test/test-util.c b/src/test/test-util.c index 3c1bac4606..dff38ab6f6 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -1539,6 +1539,7 @@ static void test_extract_first_word(void) { p = original = "\""; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\"")); + free(t); assert_se(isempty(p)); p = original = "\""; @@ -1548,6 +1549,7 @@ static void test_extract_first_word(void) { p = original = "\'"; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\'")); + free(t); assert_se(isempty(p)); p = original = "\'"; @@ -1557,6 +1559,7 @@ static void test_extract_first_word(void) { p = original = "\'fooo"; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\'fooo")); + free(t); assert_se(isempty(p)); p = original = "\'fooo"; diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 8ec6adb493..1c3f03c803 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -96,7 +96,7 @@ static void print_status_info(const StatusInfo *i) { old_tz = strdupa(tz); /* Set the new $TZ */ - if (i->timezone && setenv("TZ", i->timezone, true) < 0) + if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0) log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); else tzset(); |