summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c23
-rw-r--r--src/backlight/backlight.c2
-rw-r--r--src/basic/barrier.c1
-rw-r--r--src/basic/btrfs-util.c2
-rw-r--r--src/basic/capability-util.c71
-rw-r--r--src/basic/capability-util.h15
-rw-r--r--src/basic/cgroup-util.c9
-rw-r--r--src/basic/clock-util.c4
-rw-r--r--src/basic/errno-list.c5
-rw-r--r--src/basic/escape.c51
-rw-r--r--src/basic/escape.h2
-rw-r--r--src/basic/extract-word.c9
-rw-r--r--src/basic/fd-util.h3
-rw-r--r--src/basic/fileio.c33
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/fs-util.c2
-rw-r--r--src/basic/glob-util.c4
-rw-r--r--src/basic/in-addr-util.c4
-rw-r--r--src/basic/in-addr-util.h5
-rw-r--r--src/basic/log.c93
-rw-r--r--src/basic/log.h10
-rw-r--r--src/basic/missing.h31
-rw-r--r--src/basic/ordered-set.c64
-rw-r--r--src/basic/ordered-set.h6
-rw-r--r--src/basic/parse-util.c22
-rw-r--r--src/basic/path-util.c2
-rw-r--r--src/basic/process-util.c32
-rw-r--r--src/basic/process-util.h3
-rw-r--r--src/basic/rm-rf.c2
-rw-r--r--src/basic/signal-util.c5
-rw-r--r--src/basic/socket-label.c6
-rw-r--r--src/basic/socket-util.c4
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/basic/string-util.c30
-rw-r--r--src/basic/string-util.h3
-rw-r--r--src/basic/strv.c20
-rw-r--r--src/basic/strv.h2
-rw-r--r--src/basic/terminal-util.c17
-rw-r--r--src/basic/terminal-util.h17
-rw-r--r--src/basic/user-util.c2
-rw-r--r--src/basic/util.c2
-rw-r--r--src/boot/bootctl.c6
-rw-r--r--src/bootchart/svg.c3
-rw-r--r--src/cgtop/cgtop.c5
-rw-r--r--src/core/dbus-execute.c27
-rw-r--r--src/core/dbus-manager.c5
-rw-r--r--src/core/device.c21
-rw-r--r--src/core/execute.c137
-rw-r--r--src/core/execute.h4
-rw-r--r--src/core/job.c3
-rw-r--r--src/core/killall.c21
-rw-r--r--src/core/load-dropin.c1
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c29
-rw-r--r--src/core/load-fragment.h2
-rw-r--r--src/core/machine-id-setup.c23
-rw-r--r--src/core/machine-id-setup.h2
-rw-r--r--src/core/main.c51
-rw-r--r--src/core/manager.c6
-rw-r--r--src/core/service.c2
-rw-r--r--src/core/transaction.c32
-rw-r--r--src/core/unit.c5
-rw-r--r--src/firstboot/firstboot.c2
-rw-r--r--src/fstab-generator/fstab-generator.c4
-rw-r--r--src/getty-generator/getty-generator.c2
-rw-r--r--src/import/aufs-util.c2
-rw-r--r--src/import/import-common.c4
-rw-r--r--src/journal-remote/journal-gatewayd.c4
-rw-r--r--src/journal-remote/journal-remote.c56
-rw-r--r--src/journal-remote/journal-upload-journal.c7
-rw-r--r--src/journal-remote/microhttpd-util.h9
-rw-r--r--src/journal/compress.c85
-rw-r--r--src/journal/coredump.c27
-rw-r--r--src/journal/journal-file.c106
-rw-r--r--src/journal/journal-file.h5
-rw-r--r--src/journal/journal-send.c1
-rw-r--r--src/journal/journalctl.c2
-rw-r--r--src/journal/journald-audit.c4
-rw-r--r--src/journal/journald-kmsg.c6
-rw-r--r--src/journal/journald-server.c134
-rw-r--r--src/journal/journald-server.h3
-rw-r--r--src/journal/journald-syslog.c11
-rw-r--r--src/journal/journald.c8
-rw-r--r--src/libsystemd-network/dhcp-option.c20
-rw-r--r--src/libsystemd-network/dhcp-packet.c2
-rw-r--r--src/libsystemd-network/dhcp-protocol.h42
-rw-r--r--src/libsystemd-network/dhcp6-option.c16
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h35
-rw-r--r--src/libsystemd-network/network-internal.c15
-rw-r--r--src/libsystemd-network/network-internal.h4
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c46
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c163
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c34
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c48
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c2
-rw-r--r--src/libsystemd-network/sd-ndisc.c38
-rw-r--r--src/libsystemd-network/test-dhcp-client.c26
-rw-r--r--src/libsystemd-network/test-dhcp-option.c44
-rw-r--r--src/libsystemd-network/test-dhcp-server.c14
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c58
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c6
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h3
-rw-r--r--src/libsystemd/sd-bus/bus-error.c55
-rw-r--r--src/libsystemd/sd-bus/bus-kernel.c37
-rw-r--r--src/libsystemd/sd-bus/test-bus-error.c31
-rw-r--r--src/libsystemd/sd-device/sd-device.c2
-rw-r--r--src/libsystemd/sd-event/sd-event.c53
-rw-r--r--src/libsystemd/sd-event/test-event.c28
-rw-r--r--src/libsystemd/sd-login/sd-login.c2
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c9
-rw-r--r--src/libsystemd/sd-network/sd-network.c32
-rw-r--r--src/locale/localed.c2
-rw-r--r--src/login/loginctl.c2
-rw-r--r--src/login/logind-core.c2
-rw-r--r--src/login/logind-dbus.c98
-rw-r--r--src/login/logind-seat.c3
-rw-r--r--src/login/logind-user.c13
-rw-r--r--src/login/logind.c2
-rw-r--r--src/login/logind.conf2
-rw-r--r--src/machine-id-setup/machine-id-setup-main.c2
-rw-r--r--src/machine/machinectl.c4
-rw-r--r--src/machine/machined-dbus.c10
-rw-r--r--src/network/networkctl.c85
-rw-r--r--src/network/networkd-dhcp4.c58
-rw-r--r--src/network/networkd-link-bus.c13
-rw-r--r--src/network/networkd-link.c125
-rw-r--r--src/network/networkd-manager.c76
-rw-r--r--src/network/networkd-ndisc.c2
-rw-r--r--src/network/networkd-netdev-tunnel.c36
-rw-r--r--src/network/networkd-network-gperf.gperf30
-rw-r--r--src/network/networkd-network.c134
-rw-r--r--src/network/networkd-network.h33
-rw-r--r--src/nspawn/nspawn.c4
-rw-r--r--src/nss-mymachines/nss-mymachines.c1
-rw-r--r--src/nss-resolve/nss-resolve.c3
l---------src/resolve-host/Makefile1
-rw-r--r--src/resolve/RFCs18
-rw-r--r--src/resolve/dns-type.c45
-rw-r--r--src/resolve/dns-type.h3
-rw-r--r--src/resolve/resolve-tool.c (renamed from src/resolve-host/resolve-host.c)47
-rw-r--r--src/resolve/resolved-bus.c508
-rw-r--r--src/resolve/resolved-bus.h1
-rw-r--r--src/resolve/resolved-conf.c20
-rw-r--r--src/resolve/resolved-dns-answer.c64
-rw-r--r--src/resolve/resolved-dns-answer.h3
-rw-r--r--src/resolve/resolved-dns-cache.c46
-rw-r--r--src/resolve/resolved-dns-dnssec.c757
-rw-r--r--src/resolve/resolved-dns-dnssec.h24
-rw-r--r--src/resolve/resolved-dns-packet.c131
-rw-r--r--src/resolve/resolved-dns-packet.h2
-rw-r--r--src/resolve/resolved-dns-query.c703
-rw-r--r--src/resolve/resolved-dns-query.h16
-rw-r--r--src/resolve/resolved-dns-question.c115
-rw-r--r--src/resolve/resolved-dns-question.h12
-rw-r--r--src/resolve/resolved-dns-rr.c123
-rw-r--r--src/resolve/resolved-dns-rr.h19
-rw-r--r--src/resolve/resolved-dns-scope.c27
-rw-r--r--src/resolve/resolved-dns-scope.h2
-rw-r--r--src/resolve/resolved-dns-search-domain.c3
-rw-r--r--src/resolve/resolved-dns-search-domain.h1
-rw-r--r--src/resolve/resolved-dns-server.c175
-rw-r--r--src/resolve/resolved-dns-server.h18
-rw-r--r--src/resolve/resolved-dns-synthesize.c413
-rw-r--r--src/resolve/resolved-dns-synthesize.h30
-rw-r--r--src/resolve/resolved-dns-transaction.c575
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-dns-trust-anchor.c2
-rw-r--r--src/resolve/resolved-etc-hosts.c448
-rw-r--r--src/resolve/resolved-etc-hosts.h28
-rw-r--r--src/resolve/resolved-gperf.gperf1
-rw-r--r--src/resolve/resolved-link-bus.c552
-rw-r--r--src/resolve/resolved-link-bus.h40
-rw-r--r--src/resolve/resolved-link.c213
-rw-r--r--src/resolve/resolved-link.h12
-rw-r--r--src/resolve/resolved-manager.c57
-rw-r--r--src/resolve/resolved-manager.h13
-rw-r--r--src/resolve/resolved.conf.in1
-rw-r--r--src/resolve/test-dnssec-complex.c238
-rw-r--r--src/resolve/test-dnssec.c4
-rw-r--r--src/shared/ask-password-api.c2
-rw-r--r--src/shared/bus-util.c31
-rw-r--r--src/shared/bus-util.h2
-rw-r--r--src/shared/dns-domain.c319
-rw-r--r--src/shared/dns-domain.h6
-rw-r--r--src/shared/dropin.c2
-rw-r--r--src/shared/switch-root.c3
-rw-r--r--src/socket-proxy/socket-proxyd.c2
-rw-r--r--src/systemctl/systemctl.c53
-rw-r--r--src/systemd/sd-dhcp-client.h42
-rw-r--r--src/systemd/sd-dhcp-lease.h8
-rw-r--r--src/systemd/sd-dhcp6-client.h35
-rw-r--r--src/systemd/sd-messages.h1
-rw-r--r--src/systemd/sd-network.h18
-rw-r--r--src/systemd/sd-resolve.h6
-rw-r--r--src/sysusers/sysusers.c13
-rw-r--r--src/sysv-generator/sysv-generator.c25
-rw-r--r--src/test/test-capability.c68
-rw-r--r--src/test/test-cgroup-mask.c1
-rw-r--r--src/test/test-date.c6
-rw-r--r--src/test/test-dns-domain.c88
-rw-r--r--src/test/test-execute.c16
-rw-r--r--src/test/test-libudev.c3
-rw-r--r--src/test/test-string-util.c46
-rw-r--r--src/test/test-tmpfiles.c17
-rw-r--r--src/test/test-unit-file.c41
-rw-r--r--src/timesync/timesyncd-manager.c3
-rw-r--r--src/tmpfiles/tmpfiles.c2
-rw-r--r--src/udev/collect/collect.c3
-rw-r--r--src/udev/udev-builtin-blkid.c2
-rw-r--r--src/udev/udev-builtin-input_id.c10
-rw-r--r--src/udev/udev-builtin-net_id.c24
-rw-r--r--src/udev/udev-node.c11
-rw-r--r--src/udev/udev-watch.c7
-rw-r--r--src/udev/udevd.c3
-rw-r--r--src/vconsole/vconsole-setup.c5
215 files changed, 6907 insertions, 2632 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index b7e6255f49..95083441ab 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -39,6 +39,7 @@
static char** arg_listen = NULL;
static bool arg_accept = false;
+static bool arg_datagram = false;
static char** arg_args = NULL;
static char** arg_setenv = NULL;
static const char *arg_fdname = NULL;
@@ -98,7 +99,11 @@ static int open_sockets(int *epoll_fd, bool accept) {
STRV_FOREACH(address, arg_listen) {
- fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
+ if (arg_datagram)
+ fd = make_socket_fd(LOG_DEBUG, *address, SOCK_DGRAM, SOCK_CLOEXEC);
+ else
+ fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM, (arg_accept*SOCK_CLOEXEC));
+
if (fd < 0) {
log_open();
return log_error_errno(fd, "Failed to open '%s': %m", *address);
@@ -272,7 +277,7 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) {
}
getsockname_pretty(fd2, &local);
- getpeername_pretty(fd2, &peer);
+ getpeername_pretty(fd2, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
return launch1(name, argv, envp, fd2);
@@ -304,6 +309,7 @@ static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Listen on sockets and launch child on connection.\n\n"
"Options:\n"
+ " -d --datagram Datagram sockets\n"
" -l --listen=ADDR Listen for raw connections at ADDR\n"
" -a --accept Spawn separate child for each connection\n"
" -h --help Show this help and exit\n"
@@ -323,6 +329,7 @@ static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
+ { "datagram", no_argument, NULL, 'd' },
{ "listen", required_argument, NULL, 'l' },
{ "accept", no_argument, NULL, 'a' },
{ "setenv", required_argument, NULL, 'E' },
@@ -336,7 +343,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hl:aEd", options, NULL)) >= 0)
switch(c) {
case 'h':
help();
@@ -352,6 +359,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case 'd':
+ arg_datagram = true;
+ break;
+
case 'a':
arg_accept = true;
break;
@@ -385,6 +396,12 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_datagram && arg_accept) {
+ log_error("Datagram sockets do not accept connections. "
+ "The --datagram and --accept options may not be combined.");
+ return -EINVAL;
+ }
+
arg_args = argv + optind;
return 1 /* work to do */;
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c
index b0fa079fec..a59459bc26 100644
--- a/src/backlight/backlight.c
+++ b/src/backlight/backlight.c
@@ -323,7 +323,7 @@ int main(int argc, char *argv[]) {
errno = 0;
device = udev_device_new_from_subsystem_sysname(udev, ss, sysname);
if (!device) {
- if (errno != 0)
+ if (errno > 0)
log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname);
else
log_oom();
diff --git a/src/basic/barrier.c b/src/basic/barrier.c
index 9a78a80eb2..26ae123341 100644
--- a/src/basic/barrier.c
+++ b/src/basic/barrier.c
@@ -197,6 +197,7 @@ static bool barrier_write(Barrier *b, uint64_t buf) {
if (barrier_i_aborted(b))
return false;
+ assert(b->me >= 0);
do {
len = write(b->me, &buf, sizeof(buf));
} while (len < 0 && IN_SET(errno, EAGAIN, EINTR));
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index acd48f6954..d07d1df5a8 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -2051,7 +2051,7 @@ int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) {
args.key.nr_items = 256;
if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
- return -errno;
+ return negative_errno();
if (args.key.nr_items <= 0)
break;
diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c
index fef722b6f2..49c2d61afe 100644
--- a/src/basic/capability-util.c
+++ b/src/basic/capability-util.c
@@ -96,7 +96,62 @@ unsigned long cap_last_cap(void) {
return p;
}
-int capability_bounding_set_drop(uint64_t drop, bool right_now) {
+int capability_update_inherited_set(cap_t caps, uint64_t set) {
+ unsigned long i;
+
+ /* Add capabilities in the set to the inherited caps. Do not apply
+ * them yet. */
+
+ for (i = 0; i < cap_last_cap(); i++) {
+
+ if (set & (UINT64_C(1) << i)) {
+ cap_value_t v;
+
+ v = (cap_value_t) i;
+
+ /* Make the capability inheritable. */
+ if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
+ unsigned long i;
+ _cleanup_cap_free_ cap_t caps = NULL;
+
+ /* Add the capabilities to the ambient set. */
+
+ if (also_inherit) {
+ int r;
+ caps = cap_get_proc();
+ if (!caps)
+ return -errno;
+
+ r = capability_update_inherited_set(caps, set);
+ if (r < 0)
+ return -errno;
+
+ if (cap_set_proc(caps) < 0)
+ return -errno;
+ }
+
+ for (i = 0; i < cap_last_cap(); i++) {
+
+ if (set & (UINT64_C(1) << i)) {
+
+ /* Add the capability to the ambient set. */
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t after_cap = NULL;
cap_flag_value_t fv;
unsigned long i;
@@ -137,7 +192,7 @@ int capability_bounding_set_drop(uint64_t drop, bool right_now) {
for (i = 0; i <= cap_last_cap(); i++) {
- if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+ if (!(keep & (UINT64_C(1) << i))) {
cap_value_t v;
/* Drop it from the bounding set */
@@ -176,7 +231,7 @@ finish:
return r;
}
-static int drop_from_file(const char *fn, uint64_t drop) {
+static int drop_from_file(const char *fn, uint64_t keep) {
int r, k;
uint32_t hi, lo;
uint64_t current, after;
@@ -196,7 +251,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return -EIO;
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
- after = current & ~drop;
+ after = current & keep;
if (current == after)
return 0;
@@ -213,14 +268,14 @@ static int drop_from_file(const char *fn, uint64_t drop) {
return r;
}
-int capability_bounding_set_drop_usermode(uint64_t drop) {
+int capability_bounding_set_drop_usermode(uint64_t keep) {
int r;
- r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep);
if (r < 0)
return r;
- r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
+ r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep);
if (r < 0)
return r;
@@ -257,7 +312,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
/* Drop all caps from the bounding set, except the ones we want */
- r = capability_bounding_set_drop(~keep_capabilities, true);
+ r = capability_bounding_set_drop(keep_capabilities, true);
if (r < 0)
return log_error_errno(r, "Failed to drop capabilities: %m");
diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h
index 6bbf7318fd..be41475441 100644
--- a/src/basic/capability-util.h
+++ b/src/basic/capability-util.h
@@ -29,10 +29,15 @@
#include "macro.h"
#include "util.h"
+#define CAP_ALL (uint64_t) -1
+
unsigned long cap_last_cap(void);
int have_effective_cap(int value);
-int capability_bounding_set_drop(uint64_t drop, bool right_now);
-int capability_bounding_set_drop_usermode(uint64_t drop);
+int capability_bounding_set_drop(uint64_t keep, bool right_now);
+int capability_bounding_set_drop_usermode(uint64_t keep);
+
+int capability_ambient_set_apply(uint64_t set, bool also_inherit);
+int capability_update_inherited_set(cap_t caps, uint64_t ambient_set);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
@@ -46,3 +51,9 @@ static inline void cap_free_charpp(char **p) {
cap_free(*p);
}
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
+
+static inline bool cap_test_all(uint64_t caps) {
+ uint64_t m;
+ m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1;
+ return (caps & m) == m;
+}
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 639f9f3db1..f873fb89d3 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -53,6 +53,7 @@
#include "set.h"
#include "special.h"
#include "stat-util.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "unit-name.h"
@@ -92,7 +93,7 @@ int cg_read_pid(FILE *f, pid_t *_pid) {
if (feof(f))
return 0;
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
}
if (ul <= 0)
@@ -647,7 +648,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
if (errno == ENOENT)
r = 0;
- else if (errno != 0)
+ else if (errno > 0)
r = -errno;
else
r = -EIO;
@@ -716,7 +717,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
if (pid == 0)
pid = getpid();
- snprintf(c, sizeof(c), PID_FMT"\n", pid);
+ xsprintf(c, PID_FMT "\n", pid);
return write_string_file(fs, c, 0);
}
@@ -2090,7 +2091,7 @@ int cg_kernel_controllers(Set *controllers) {
if (feof(f))
break;
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EBADMSG;
diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c
index 00f549c023..05788a360e 100644
--- a/src/basic/clock-util.c
+++ b/src/basic/clock-util.c
@@ -33,6 +33,7 @@
#include "fd-util.h"
#include "macro.h"
#include "string-util.h"
+#include "util.h"
int clock_get_hwclock(struct tm *tm) {
_cleanup_close_ int fd = -1;
@@ -121,7 +122,8 @@ int clock_set_timezone(int *min) {
* have read from the RTC.
*/
if (settimeofday(tv_null, &tz) < 0)
- return -errno;
+ return negative_errno();
+
if (min)
*min = minutesdelta;
return 0;
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 0a66902ac9..b4d080103b 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -25,7 +25,7 @@
#include "macro.h"
static const struct errno_name* lookup_errno(register const char *str,
- register unsigned int len);
+ register unsigned int len);
#include "errno-from-name.h"
#include "errno-to-name.h"
@@ -48,8 +48,9 @@ int errno_from_name(const char *name) {
sc = lookup_errno(name, strlen(name));
if (!sc)
- return 0;
+ return -EINVAL;
+ assert(sc->id > 0);
return sc->id;
}
diff --git a/src/basic/escape.c b/src/basic/escape.c
index ab282efa3c..5661f36813 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -119,16 +119,18 @@ char *cescape(const char *s) {
return cescape_length(s, strlen(s));
}
-int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit) {
int r = 1;
assert(p);
assert(*p);
assert(ret);
- /* Unescapes C style. Returns the unescaped character in ret,
- * unless we encountered a \u sequence in which case the full
- * unicode character is returned in ret_unicode, instead. */
+ /* Unescapes C style. Returns the unescaped character in ret.
+ * Sets *eight_bit to true if the escaped sequence either fits in
+ * one byte in UTF-8 or is a non-unicode literal byte and should
+ * instead be copied directly.
+ */
if (length != (size_t) -1 && length < 1)
return -EINVAL;
@@ -190,7 +192,8 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode
if (a == 0 && b == 0)
return -EINVAL;
- *ret = (char) ((a << 4U) | b);
+ *ret = (a << 4U) | b;
+ *eight_bit = true;
r = 3;
break;
}
@@ -217,16 +220,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode
if (c == 0)
return -EINVAL;
- if (c < 128)
- *ret = c;
- else {
- if (!ret_unicode)
- return -EINVAL;
-
- *ret = 0;
- *ret_unicode = c;
- }
-
+ *ret = c;
r = 5;
break;
}
@@ -258,16 +252,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode
if (!unichar_is_valid(c))
return -EINVAL;
- if (c < 128)
- *ret = c;
- else {
- if (!ret_unicode)
- return -EINVAL;
-
- *ret = 0;
- *ret_unicode = c;
- }
-
+ *ret = c;
r = 9;
break;
}
@@ -309,6 +294,7 @@ int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode
return -EINVAL;
*ret = m;
+ *eight_bit = true;
r = 3;
break;
}
@@ -342,7 +328,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
for (f = s, t = r + pl; f < s + length; f++) {
size_t remaining;
uint32_t u;
- char c;
+ bool eight_bit = false;
int k;
remaining = s + length - f;
@@ -365,7 +351,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
return -EINVAL;
}
- k = cunescape_one(f + 1, remaining - 1, &c, &u);
+ k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
if (k < 0) {
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
@@ -377,14 +363,13 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
return k;
}
- if (c != 0)
- /* Non-Unicode? Let's encode this directly */
- *(t++) = c;
+ f += k;
+ if (eight_bit)
+ /* One byte? Set directly as specified */
+ *(t++) = u;
else
- /* Unicode? Then let's encode this in UTF-8 */
+ /* Otherwise encode as multi-byte UTF-8 */
t += utf8_encode_unichar(t, u);
-
- f += k;
}
*t = 0;
diff --git a/src/basic/escape.h b/src/basic/escape.h
index c710f01743..d943aa71f5 100644
--- a/src/basic/escape.h
+++ b/src/basic/escape.h
@@ -45,7 +45,7 @@ size_t cescape_char(char c, char *buf);
int cunescape(const char *s, UnescapeFlags flags, char **ret);
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
-int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode);
+int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit);
char *xescape(const char *s, const char *bad);
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
index 7cc2a1de13..090d2a7884 100644
--- a/src/basic/extract-word.c
+++ b/src/basic/extract-word.c
@@ -108,8 +108,9 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
if (flags & EXTRACT_CUNESCAPE) {
uint32_t u;
+ bool eight_bit = false;
- r = cunescape_one(*p, (size_t) -1, &c, &u);
+ r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
if (r < 0) {
if (flags & EXTRACT_CUNESCAPE_RELAX) {
s[sz++] = '\\';
@@ -119,10 +120,10 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
} else {
(*p) += r - 1;
- if (c != 0)
- s[sz++] = c; /* normal explicit char */
+ if (eight_bit)
+ s[sz++] = u;
else
- sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
+ sz += utf8_encode_unichar(s + sz, u);
}
} else
s[sz++] = c;
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 973413ff42..20890e3279 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -74,5 +74,6 @@ void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
+/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)
+ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 3a237252b5..3ff70310e1 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -165,7 +165,7 @@ int read_one_line_file(const char *fn, char **line) {
if (!fgets(t, sizeof(t), f)) {
if (ferror(f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
t[0] = 0;
}
@@ -1064,7 +1064,7 @@ int fflush_and_check(FILE *f) {
fflush(f);
if (ferror(f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -1251,3 +1251,32 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
*ret = (usec_t) t;
return 0;
}
+
+int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
+ int r;
+
+ assert(s);
+
+ /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
+ * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
+ * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
+ * element, but not before the first one. */
+
+ if (!f)
+ f = stdout;
+
+ if (space) {
+ if (!separator)
+ separator = " ";
+
+ if (*space) {
+ r = fputs(separator, f);
+ if (r < 0)
+ return r;
+ }
+
+ *space = true;
+ }
+
+ return fputs(s, f);
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 95e8698941..9e09574133 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -82,3 +82,5 @@ int tempfn_random_child(const char *p, const char *extra, char **ret);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
+
+int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index fb760abe18..d31bd6e273 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -481,7 +481,7 @@ int get_files_in_directory(const char *path, char ***list) {
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
break;
diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c
index a0be0efd40..811ab6ec36 100644
--- a/src/basic/glob-util.c
+++ b/src/basic/glob-util.c
@@ -40,7 +40,7 @@ int glob_exists(const char *path) {
if (k == GLOB_NOSPACE)
return -ENOMEM;
if (k != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return !strv_isempty(g.gl_pathv);
}
@@ -58,7 +58,7 @@ int glob_extend(char ***strv, const char *path) {
if (k == GLOB_NOSPACE)
return -ENOMEM;
if (k != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
if (strv_isempty(g.gl_pathv))
return -ENOENT;
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index 5143dddf8f..8609ffb3c9 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -219,7 +219,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
errno = 0;
if (!inet_ntop(family, u, x, l)) {
free(x);
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
*ret = x;
@@ -236,7 +236,7 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
errno = 0;
if (inet_pton(family, s, ret) <= 0)
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
return 0;
}
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index bcc116c783..f2b8865df5 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -33,6 +33,11 @@ union in_addr_union {
struct in6_addr in6;
};
+struct in_addr_data {
+ int family;
+ union in_addr_union address;
+};
+
int in_addr_is_null(int family, const union in_addr_union *u);
int in_addr_is_link_local(int family, const union in_addr_union *u);
int in_addr_is_localhost(int family, const union in_addr_union *u);
diff --git a/src/basic/log.c b/src/basic/log.c
index 1a9e6bdb91..18d4b82be2 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -352,7 +352,7 @@ static int write_to_console(
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
if (show_location) {
- snprintf(location, sizeof(location), "(%s:%i) ", file, line);
+ xsprintf(location, "(%s:%i) ", file, line);
IOVEC_SET_STRING(iovec[n++], location);
}
@@ -777,7 +777,7 @@ static void log_assert(
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+ xsprintf(buffer, format, text, file, line, func);
REENABLE_WARNING;
log_abort_msg = buffer;
@@ -805,6 +805,52 @@ int log_oom_internal(const char *file, int line, const char *func) {
return -ENOMEM;
}
+int log_format_iovec(
+ struct iovec *iovec,
+ unsigned iovec_len,
+ unsigned *n,
+ bool newline_separator,
+ int error,
+ const char *format,
+ va_list ap) {
+
+ static const char nl = '\n';
+
+ while (format && *n + 1 < iovec_len) {
+ va_list aq;
+ char *m;
+ int r;
+
+ /* We need to copy the va_list structure,
+ * since vasprintf() leaves it afterwards at
+ * an undefined location */
+
+ if (error != 0)
+ errno = error;
+
+ va_copy(aq, ap);
+ r = vasprintf(&m, format, aq);
+ va_end(aq);
+ if (r < 0)
+ return -EINVAL;
+
+ /* Now, jump enough ahead, so that we point to
+ * the next format string */
+ VA_FORMAT_ADVANCE(format, ap);
+
+ IOVEC_SET_STRING(iovec[(*n)++], m);
+
+ if (newline_separator) {
+ iovec[*n].iov_base = (char*) &nl;
+ iovec[*n].iov_len = 1;
+ (*n)++;
+ }
+
+ format = va_arg(ap, char *);
+ }
+ return 0;
+}
+
int log_struct_internal(
int level,
int error,
@@ -837,10 +883,10 @@ int log_struct_internal(
char header[LINE_MAX];
struct iovec iovec[17] = {};
unsigned n = 0, i;
+ int r;
struct msghdr mh = {
.msg_iov = iovec,
};
- static const char nl = '\n';
bool fallback = false;
/* If the journal is available do structured logging */
@@ -848,43 +894,14 @@ int log_struct_internal(
IOVEC_SET_STRING(iovec[n++], header);
va_start(ap, format);
- while (format && n + 1 < ELEMENTSOF(iovec)) {
- va_list aq;
- char *m;
-
- /* We need to copy the va_list structure,
- * since vasprintf() leaves it afterwards at
- * an undefined location */
-
- if (error != 0)
- errno = error;
-
- va_copy(aq, ap);
- if (vasprintf(&m, format, aq) < 0) {
- va_end(aq);
- fallback = true;
- goto finish;
- }
- va_end(aq);
-
- /* Now, jump enough ahead, so that we point to
- * the next format string */
- VA_FORMAT_ADVANCE(format, ap);
-
- IOVEC_SET_STRING(iovec[n++], m);
-
- iovec[n].iov_base = (char*) &nl;
- iovec[n].iov_len = 1;
- n++;
-
- format = va_arg(ap, char *);
+ r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap);
+ if (r < 0)
+ fallback = true;
+ else {
+ mh.msg_iovlen = n;
+ (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
}
- mh.msg_iovlen = n;
-
- (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
-
- finish:
va_end(ap);
for (i = 1; i < n; i += 2)
free(iovec[i].iov_base);
diff --git a/src/basic/log.h b/src/basic/log.h
index cda1e45cc8..8c7c5e4598 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -26,6 +26,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <sys/signalfd.h>
+#include <sys/socket.h>
#include <syslog.h>
#include "sd-id128.h"
@@ -127,6 +128,15 @@ int log_oom_internal(
int line,
const char *func);
+int log_format_iovec(
+ struct iovec *iovec,
+ unsigned iovec_len,
+ unsigned *n,
+ bool newline_separator,
+ int error,
+ const char *format,
+ va_list ap);
+
/* This modifies the buffer passed! */
int log_dump_internal(
int level,
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 880e724cb4..6ed2133ed1 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -974,6 +974,10 @@ static inline int setns(int fd, int nstype) {
#define IFA_FLAGS 8
#endif
+#ifndef IFA_F_MANAGETEMPADDR
+#define IFA_F_MANAGETEMPADDR 0x100
+#endif
+
#ifndef IFA_F_NOPREFIXROUTE
#define IFA_F_NOPREFIXROUTE 0x200
#endif
@@ -1129,3 +1133,30 @@ static inline key_serial_t request_key(const char *type, const char *description
#ifndef KEY_SPEC_USER_KEYRING
#define KEY_SPEC_USER_KEYRING -4
#endif
+
+#ifndef PR_CAP_AMBIENT
+#define PR_CAP_AMBIENT 47
+#endif
+
+#ifndef PR_CAP_AMBIENT_IS_SET
+#define PR_CAP_AMBIENT_IS_SET 1
+#endif
+
+#ifndef PR_CAP_AMBIENT_RAISE
+#define PR_CAP_AMBIENT_RAISE 2
+#endif
+
+#ifndef PR_CAP_AMBIENT_CLEAR_ALL
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+#endif
+
+/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway,
+ * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with
+ * linux/if.h. */
+#ifndef IF_OPER_UNKNOWN
+#define IF_OPER_UNKNOWN 0
+#endif
+
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c
new file mode 100644
index 0000000000..2e0bdf6488
--- /dev/null
+++ b/src/basic/ordered-set.c
@@ -0,0 +1,64 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "ordered-set.h"
+#include "strv.h"
+
+int ordered_set_consume(OrderedSet *s, void *p) {
+ int r;
+
+ r = ordered_set_put(s, p);
+ if (r <= 0)
+ free(p);
+
+ return r;
+}
+
+int ordered_set_put_strdup(OrderedSet *s, const char *p) {
+ char *c;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ c = strdup(p);
+ if (!c)
+ return -ENOMEM;
+
+ r = ordered_set_consume(s, c);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+int ordered_set_put_strdupv(OrderedSet *s, char **l) {
+ int n = 0, r;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = ordered_set_put_strdup(s, *i);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ return n;
+}
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
index da10e90ff2..ab185c11aa 100644
--- a/src/basic/ordered-set.h
+++ b/src/basic/ordered-set.h
@@ -62,9 +62,15 @@ static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value)
return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL);
}
+int ordered_set_consume(OrderedSet *s, void *p);
+int ordered_set_put_strdup(OrderedSet *s, const char *p);
+int ordered_set_put_strdupv(OrderedSet *s, char **l);
+
#define ORDERED_SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); )
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
+#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 618ef5d564..d8de6f90ea 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -81,7 +81,7 @@ int parse_mode(const char *s, mode_t *ret) {
errno = 0;
l = strtol(s, &x, 8);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -176,7 +176,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
errno = 0;
l = strtoull(p, &e, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (e == p)
return -EINVAL;
@@ -192,7 +192,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
char *e2;
l2 = strtoull(e, &e2, 10);
- if (errno != 0)
+ if (errno > 0)
return -errno;
/* Ignore failure. E.g. 10.M is valid */
@@ -330,7 +330,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
errno = 0;
l = strtoul(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -352,7 +352,7 @@ int safe_atoi(const char *s, int *ret_i) {
errno = 0;
l = strtol(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -374,7 +374,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
errno = 0;
l = strtoull(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -394,7 +394,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
errno = 0;
l = strtoll(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -414,7 +414,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
errno = 0;
l = strtoul(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -438,7 +438,7 @@ int safe_atou16(const char *s, uint16_t *ret) {
errno = 0;
l = strtoul(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -460,7 +460,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
errno = 0;
l = strtol(s, &x, 0);
- if (errno != 0)
+ if (errno > 0)
return -errno;
if (!x || x == s || *x)
return -EINVAL;
@@ -485,7 +485,7 @@ int safe_atod(const char *s, double *ret_d) {
errno = 0;
d = strtod_l(s, &x, loc);
- if (errno != 0) {
+ if (errno > 0) {
freelocale(loc);
return -errno;
}
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 61fab0e087..4837bb2d7d 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -102,7 +102,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
cwd = get_current_dir_name();
if (!cwd)
- return -errno;
+ return negative_errno();
c = strjoin(cwd, "/", p, NULL);
}
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 4cc54a51fb..189ef9ab60 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -33,6 +33,9 @@
#include <sys/wait.h>
#include <syslog.h>
#include <unistd.h>
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
#include "alloc-util.h"
#include "escape.h"
@@ -45,6 +48,7 @@
#include "missing.h"
#include "process-util.h"
#include "signal-util.h"
+#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
#include "user-util.h"
@@ -634,6 +638,17 @@ bool pid_is_alive(pid_t pid) {
return true;
}
+int pid_from_same_root_fs(pid_t pid) {
+ const char *root;
+
+ if (pid < 0)
+ return 0;
+
+ root = procfs_file_alloca(pid, "root");
+
+ return files_same(root, "/proc/1/root");
+}
+
bool is_main_thread(void) {
static thread_local int cached = 0;
@@ -730,6 +745,23 @@ const char* personality_to_string(unsigned long p) {
return NULL;
}
+void valgrind_summary_hack(void) {
+#ifdef HAVE_VALGRIND_VALGRIND_H
+ if (getpid() == 1 && RUNNING_ON_VALGRIND) {
+ pid_t pid;
+ pid = raw_clone(SIGCHLD, NULL);
+ if (pid < 0)
+ log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
+ else if (pid == 0)
+ exit(EXIT_SUCCESS);
+ else {
+ log_info("Spawned valgrind helper as PID "PID_FMT".", pid);
+ (void) wait_for_terminate(pid, NULL);
+ }
+ }
+#endif
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index f4c4437624..f5d193e762 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -70,6 +70,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value);
bool pid_is_alive(pid_t pid);
bool pid_is_unwaited(pid_t pid);
+int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
@@ -98,3 +99,5 @@ int sched_policy_from_string(const char *s);
#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+
+void valgrind_summary_hack(void);
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index 14f8474da0..4807561723 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -82,7 +82,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
errno = 0;
de = readdir(d);
if (!de) {
- if (errno != 0 && ret == 0)
+ if (errno > 0 && ret == 0)
ret = -errno;
return ret;
}
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index 7637fccb2f..315efadd93 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -26,6 +26,7 @@
#include "macro.h"
#include "parse-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -234,9 +235,9 @@ const char *signal_to_string(int signo) {
return name;
if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN);
else
- snprintf(buf, sizeof(buf), "%d", signo);
+ xsprintf(buf, "%d", signo);
return buf;
}
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
index e169439e04..bd206586ce 100644
--- a/src/basic/socket-label.c
+++ b/src/basic/socket-label.c
@@ -147,7 +147,7 @@ int socket_address_listen(
return r;
}
-int make_socket_fd(int log_level, const char* address, int flags) {
+int make_socket_fd(int log_level, const char* address, int type, int flags) {
SocketAddress a;
int fd, r;
@@ -155,7 +155,9 @@ int make_socket_fd(int log_level, const char* address, int flags) {
if (r < 0)
return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
- fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
+ a.type = type;
+
+ fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
NULL, false, false, false, 0755, 0644, NULL);
if (fd < 0 || log_get_max_level() >= log_level) {
_cleanup_free_ char *p = NULL;
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index be144e157d..f2bb3bab86 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -601,7 +601,7 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
return 0;
}
-int getpeername_pretty(int fd, char **ret) {
+int getpeername_pretty(int fd, bool include_port, char **ret) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
int r;
@@ -631,7 +631,7 @@ int getpeername_pretty(int fd, char **ret) {
/* For remote sockets we translate IPv6 addresses back to IPv4
* if applicable, since that's nicer. */
- return sockaddr_pretty(&sa.sa, salen, true, true, ret);
+ return sockaddr_pretty(&sa.sa, salen, true, include_port, ret);
}
int getsockname_pretty(int fd, char **ret) {
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 6da1df68d8..2323ccf3ab 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -89,7 +89,7 @@ int socket_address_listen(
mode_t directory_mode,
mode_t socket_mode,
const char *label);
-int make_socket_fd(int log_level, const char* address, int flags);
+int make_socket_fd(int log_level, const char* address, int type, int flags);
bool socket_address_is(const SocketAddress *a, const char *s, int type);
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
@@ -105,7 +105,7 @@ bool socket_ipv6_is_supported(void);
int sockaddr_port(const struct sockaddr *_sa) _pure_;
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
-int getpeername_pretty(int fd, char **ret);
+int getpeername_pretty(int fd, bool include_port, char **ret);
int getsockname_pretty(int fd, char **ret);
int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret);
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 849e457439..1f95a9abba 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -348,6 +348,36 @@ char *ascii_strlower_n(char *t, size_t n) {
return t;
}
+int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
+
+ for (; n > 0; a++, b++, n--) {
+ int x, y;
+
+ x = (int) (uint8_t) ascii_tolower(*a);
+ y = (int) (uint8_t) ascii_tolower(*b);
+
+ if (x != y)
+ return x - y;
+ }
+
+ return 0;
+}
+
+int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
+ int r;
+
+ r = ascii_strcasecmp_n(a, b, MIN(n, m));
+ if (r != 0)
+ return r;
+
+ if (n < m)
+ return -1;
+ else if (n > m)
+ return 1;
+ else
+ return 0;
+}
+
bool chars_intersect(const char *a, const char *b) {
const char *p;
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index 1ac6bcd6f8..8ea18f45aa 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -134,6 +134,9 @@ char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
+int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
+int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
+
bool chars_intersect(const char *a, const char *b) _pure_;
static inline bool _pure_ in_charset(const char *s, const char* charset) {
diff --git a/src/basic/strv.c b/src/basic/strv.c
index 0a3d15706f..5532c53ad1 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
+#include "fileio.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -871,3 +872,22 @@ rollback:
nl[k] = NULL;
return -ENOMEM;
}
+
+int fputstrv(FILE *f, char **l, const char *separator, bool *space) {
+ bool b = false;
+ char **s;
+ int r;
+
+ /* Like fputs(), but for strv, and with a less stupid argument order */
+
+ if (!space)
+ space = &b;
+
+ STRV_FOREACH(s, l) {
+ r = fputs_with_space(f, *s, separator, space);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/basic/strv.h b/src/basic/strv.h
index bb61db2638..560f90115c 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -169,3 +169,5 @@ char ***strv_free_free(char ***l);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
+
+int fputstrv(FILE *f, char **l, const char *separator, bool *space);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index a39764472b..fedfc8a5df 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -128,7 +128,7 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
errno = 0;
if (!fgets(line, sizeof(line), f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
truncate_nl(line);
@@ -212,7 +212,7 @@ int ask_string(char **ret, const char *text, ...) {
errno = 0;
if (!fgets(line, sizeof(line), stdin))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
if (!endswith(line, "\n"))
putchar('\n');
@@ -1135,3 +1135,16 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
return receive_one_fd(pair[0], 0);
}
+
+bool colors_enabled(void) {
+ const char *colors;
+
+ colors = getenv("SYSTEMD_COLORS");
+ if (!colors) {
+ if (streq_ptr(getenv("TERM"), "dumb"))
+ return false;
+ return on_tty();
+ }
+
+ return parse_boolean(colors) != 0;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 597a0060ad..a7c96a77cb 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -79,37 +79,38 @@ unsigned lines(void);
void columns_lines_cache_reset(int _unused_ signum);
bool on_tty(void);
+bool colors_enabled(void);
static inline const char *ansi_underline(void) {
- return on_tty() ? ANSI_UNDERLINE : "";
+ return colors_enabled() ? ANSI_UNDERLINE : "";
}
static inline const char *ansi_highlight(void) {
- return on_tty() ? ANSI_HIGHLIGHT : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT : "";
}
static inline const char *ansi_highlight_underline(void) {
- return on_tty() ? ANSI_HIGHLIGHT_UNDERLINE : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : "";
}
static inline const char *ansi_highlight_red(void) {
- return on_tty() ? ANSI_HIGHLIGHT_RED : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_RED : "";
}
static inline const char *ansi_highlight_green(void) {
- return on_tty() ? ANSI_HIGHLIGHT_GREEN : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : "";
}
static inline const char *ansi_highlight_yellow(void) {
- return on_tty() ? ANSI_HIGHLIGHT_YELLOW : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : "";
}
static inline const char *ansi_highlight_blue(void) {
- return on_tty() ? ANSI_HIGHLIGHT_BLUE : "";
+ return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : "";
}
static inline const char *ansi_normal(void) {
- return on_tty() ? ANSI_NORMAL : "";
+ return colors_enabled() ? ANSI_NORMAL : "";
}
int get_ctty_devnr(pid_t pid, dev_t *d);
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index 56e1a3be48..70a6e1f5e4 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -68,7 +68,7 @@ int parse_uid(const char *s, uid_t *ret) {
if (!uid_is_valid(uid))
return -ENXIO; /* we return ENXIO instead of EINVAL
* here, to make it easy to distuingish
- * invalid numeric uids invalid
+ * invalid numeric uids from invalid
* strings. */
if (ret)
diff --git a/src/basic/util.c b/src/basic/util.c
index 9e0b576283..4434ecfdf6 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -513,7 +513,7 @@ int on_ac_power(void) {
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 4cf42d17f3..77eea6aada 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -270,9 +270,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
if (r < 0)
return r;
if (r > 0)
- printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v);
+ printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v);
else
- printf(" File: └─/%s/%s\n", path, de->d_name);
+ printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name);
c++;
}
@@ -324,7 +324,7 @@ static int print_efi_option(uint16_t id, bool in_order) {
printf(" ID: 0x%04X\n", id);
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition));
- printf(" File: └─%s\n", path);
+ printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path);
printf("\n");
return 0;
diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c
index 2bf473ffc1..79e261abe5 100644
--- a/src/bootchart/svg.c
+++ b/src/bootchart/svg.c
@@ -37,6 +37,7 @@
#include "fileio.h"
#include "list.h"
#include "macro.h"
+#include "stdio-util.h"
#include "store.h"
#include "svg.h"
#include "utf8.h"
@@ -171,7 +172,7 @@ static int svg_title(FILE *of, const char *build, int pscount, double log_start,
strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1);
rootbdev[3] = '\0';
- snprintf(filename, sizeof(filename), "/sys/block/%s/device/model", rootbdev);
+ xsprintf(filename, "/sys/block/%s/device/model", rootbdev);
r = read_one_line_file(filename, &model);
if (r < 0)
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 0a5c11ad0c..4894296554 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -40,6 +40,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "stdio-util.h"
#include "terminal-util.h"
#include "unit-name.h"
#include "util.h"
@@ -565,9 +566,9 @@ static void display(Hashmap *a) {
}
if (arg_cpu_type == CPU_PERCENT)
- snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
+ xsprintf(buffer, "%6s", "%CPU");
else
- snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
+ xsprintf(buffer, "%*s", maxtcpu, "CPU Time");
rows = lines();
if (rows <= 10)
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 1f736b2686..eae0808f9e 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -141,7 +141,7 @@ static int property_get_nice(
else {
errno = 0;
n = getpriority(PRIO_PROCESS, 0);
- if (errno != 0)
+ if (errno > 0)
n = 0;
}
@@ -293,9 +293,25 @@ static int property_get_capability_bounding_set(
assert(reply);
assert(c);
- /* We store this negated internally, to match the kernel, but
- * we expose it normalized. */
- return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
+ return sd_bus_message_append(reply, "t", c->capability_bounding_set);
+}
+
+static int property_get_ambient_capabilities(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "t", c->capability_ambient_set);
}
static int property_get_capabilities(
@@ -689,6 +705,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1365,7 +1382,7 @@ int bus_exec_context_set_transient_property(
dirs = &c->read_write_dirs;
else if (streq(name, "ReadOnlyDirectories"))
dirs = &c->read_only_dirs;
- else if (streq(name, "InaccessibleDirectories"))
+ else /* "InaccessibleDirectories" */
dirs = &c->inaccessible_dirs;
if (strv_length(l) == 0) {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 8a523cc8ac..c5c672a0a2 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1607,6 +1607,7 @@ static int reply_unit_file_changes_and_free(
if (r < 0)
goto fail;
+ unit_file_changes_free(changes, n_changes);
return sd_bus_send(NULL, reply, NULL);
fail:
@@ -1843,8 +1844,10 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
- if (r < 0)
+ if (r < 0) {
+ unit_file_changes_free(changes, n_changes);
return r;
+ }
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
diff --git a/src/core/device.c b/src/core/device.c
index bcd4d1146b..807547c87f 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -267,7 +267,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
assert(u);
assert(dev);
- property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS";
+ property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
wants = udev_device_get_property_value(dev, property);
if (!wants)
return 0;
@@ -315,12 +315,19 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
u = manager_get_unit(m, e);
- if (u &&
- sysfs &&
- DEVICE(u)->sysfs &&
- !path_equal(DEVICE(u)->sysfs, sysfs)) {
- log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
- return -EEXIST;
+ /* The device unit can still be present even if the device was
+ * unplugged: a mount unit can reference it hence preventing
+ * the GC to have garbaged it. That's desired since the device
+ * unit may have a dependency on the mount unit which was
+ * added during the loading of the later. */
+ if (u && DEVICE(u)->state == DEVICE_PLUGGED) {
+ /* This unit is in plugged state: we're sure it's
+ * attached to a device. */
+ if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
+ log_unit_error(u, "Dev %s appeared twice with different sysfs paths %s and %s",
+ e, DEVICE(u)->sysfs, sysfs);
+ return -EEXIST;
+ }
}
if (!u) {
diff --git a/src/core/execute.c b/src/core/execute.c
index 9b76861919..b9de2617a9 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -737,12 +737,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* Sets (but doesn't lookup) the uid and make sure we keep the
* capabilities while doing so. */
- if (context->capabilities) {
- _cleanup_cap_free_ cap_t d = NULL;
- static const cap_value_t bits[] = {
- CAP_SETUID, /* Necessary so that we can run setresuid() below */
- CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
- };
+ if (context->capabilities || context->capability_ambient_set != 0) {
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
@@ -758,16 +753,24 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* Second step: set the capabilities. This will reduce
* the capabilities to the minimum we need. */
- d = cap_dup(context->capabilities);
- if (!d)
- return -errno;
+ if (context->capabilities) {
+ _cleanup_cap_free_ cap_t d = NULL;
+ static const cap_value_t bits[] = {
+ CAP_SETUID, /* Necessary so that we can run setresuid() below */
+ CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
+ };
- if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
- return -errno;
+ d = cap_dup(context->capabilities);
+ if (!d)
+ return -errno;
- if (cap_set_proc(d) < 0)
- return -errno;
+ if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
+ cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
+ return -errno;
+
+ if (cap_set_proc(d) < 0)
+ return -errno;
+ }
}
/* Third step: actually set the uids */
@@ -811,8 +814,7 @@ static int setup_pam(
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
pam_handle_t *handle = NULL;
sigset_t old_ss;
- int pam_code = PAM_SUCCESS;
- int err = 0;
+ int pam_code = PAM_SUCCESS, r;
char **e = NULL;
bool close_session = false;
pid_t pam_pid = 0, parent_pid;
@@ -829,8 +831,8 @@ static int setup_pam(
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
- err = barrier_create(&barrier);
- if (err < 0)
+ r = barrier_create(&barrier);
+ if (r < 0)
goto fail;
if (log_get_max_level() < LOG_DEBUG)
@@ -872,12 +874,13 @@ static int setup_pam(
parent_pid = getpid();
pam_pid = fork();
- if (pam_pid < 0)
+ if (pam_pid < 0) {
+ r = -errno;
goto fail;
+ }
if (pam_pid == 0) {
- int sig;
- int r = EXIT_PAM;
+ int sig, ret = EXIT_PAM;
/* The child's job is to reset the PAM session on
* termination */
@@ -942,11 +945,11 @@ static int setup_pam(
goto child_finish;
}
- r = 0;
+ ret = 0;
child_finish:
pam_end(handle, pam_code | flags);
- _exit(r);
+ _exit(ret);
}
barrier_set_role(&barrier, BARRIER_PARENT);
@@ -975,10 +978,9 @@ static int setup_pam(
fail:
if (pam_code != PAM_SUCCESS) {
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
- err = -EPERM; /* PAM errors do not map to errno */
- } else {
- err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
- }
+ r = -EPERM; /* PAM errors do not map to errno */
+ } else
+ log_error_errno(r, "PAM failed: %m");
if (handle) {
if (close_session)
@@ -988,15 +990,9 @@ fail:
}
strv_free(e);
-
closelog();
- if (pam_pid > 1) {
- kill(pam_pid, SIGTERM);
- kill(pam_pid, SIGCONT);
- }
-
- return err;
+ return r;
}
#endif
@@ -1856,6 +1852,8 @@ static int exec_child(
if (params->apply_permissions) {
+ int secure_bits = context->secure_bits;
+
for (i = 0; i < _RLIMIT_MAX; i++) {
if (!context->rlimit[i])
continue;
@@ -1866,12 +1864,37 @@ static int exec_child(
}
}
- if (context->capability_bounding_set_drop) {
- r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
+ if (!cap_test_all(context->capability_bounding_set)) {
+ r = capability_bounding_set_drop(context->capability_bounding_set, false);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+ }
+
+ /* This is done before enforce_user, but ambient set
+ * does not survive over setresuid() if keep_caps is not set. */
+ if (context->capability_ambient_set != 0) {
+ r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
+
+ if (context->capabilities) {
+
+ /* The capabilities in ambient set need to be also in the inherited
+ * set. If they aren't, trying to get them will fail. Add the ambient
+ * set inherited capabilities to the capability set in the context.
+ * This is needed because if capabilities are set (using "Capabilities="
+ * keyword), they will override whatever we set now. */
+
+ r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+ }
}
if (context->user) {
@@ -1880,14 +1903,32 @@ static int exec_child(
*exit_status = EXIT_USER;
return r;
}
+ if (context->capability_ambient_set != 0) {
+
+ /* Fix the ambient capabilities after user change. */
+ r = capability_ambient_set_apply(context->capability_ambient_set, false);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+
+ /* If we were asked to change user and ambient capabilities
+ * were requested, we had to add keep-caps to the securebits
+ * so that we would maintain the inherited capability set
+ * through the setresuid(). Make sure that the bit is added
+ * also to the context secure_bits so that we don't try to
+ * drop the bit away next. */
+
+ secure_bits |= 1<<SECURE_KEEP_CAPS;
+ }
}
/* PR_GET_SECUREBITS is not privileged, while
* PR_SET_SECUREBITS is. So to suppress
* potential EPERMs we'll try not to call
* PR_SET_SECUREBITS unless necessary. */
- if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
- if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+ if (prctl(PR_GET_SECUREBITS) != secure_bits)
+ if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
return -errno;
}
@@ -2114,6 +2155,7 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
+ c->capability_bounding_set = CAP_ALL;
}
void exec_context_done(ExecContext *c) {
@@ -2270,7 +2312,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
continue;
strv_free(r);
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
count = pglob.gl_pathc;
if (count == 0) {
@@ -2517,12 +2559,23 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
- if (c->capability_bounding_set_drop) {
+ if (c->capability_bounding_set != CAP_ALL) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
- if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
+ if (c->capability_bounding_set & (UINT64_C(1) << l))
+ fprintf(f, " %s", strna(capability_to_name(l)));
+
+ fputs("\n", f);
+ }
+
+ if (c->capability_ambient_set != 0) {
+ unsigned long l;
+ fprintf(f, "%sAmbientCapabilities:", prefix);
+
+ for (l = 0; l <= cap_last_cap(); l++)
+ if (c->capability_ambient_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
@@ -2623,7 +2676,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
fputc('\n', f);
}
- if (c->syscall_errno != 0)
+ if (c->syscall_errno > 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
diff --git a/src/core/execute.h b/src/core/execute.h
index be5be9f531..8649620830 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -155,7 +155,9 @@ struct ExecContext {
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
unsigned long mount_flags;
- uint64_t capability_bounding_set_drop;
+ uint64_t capability_bounding_set;
+
+ uint64_t capability_ambient_set;
cap_t capabilities;
int secure_bits;
diff --git a/src/core/job.c b/src/core/job.c
index 9654590635..274c554da9 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -35,6 +35,7 @@
#include "parse-util.h"
#include "set.h"
#include "special.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -754,7 +755,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
switch (t) {
diff --git a/src/core/killall.c b/src/core/killall.c
index 77f145b4d1..d0c7c89670 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -37,7 +37,7 @@
#define TIMEOUT_USEC (10 * USEC_PER_SEC)
-static bool ignore_proc(pid_t pid) {
+static bool ignore_proc(pid_t pid, bool warn_rootfs) {
_cleanup_fclose_ FILE *f = NULL;
char c;
const char *p;
@@ -72,7 +72,22 @@ static bool ignore_proc(pid_t pid) {
* spree.
*
* http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
- if (count == 1 && c == '@')
+ if (c == '@' && warn_rootfs) {
+ _cleanup_free_ char *comm = NULL;
+
+ r = pid_from_same_root_fs(pid);
+ if (r < 0)
+ return true;
+
+ get_process_comm(pid, &comm);
+
+ if (r)
+ log_notice("Process " PID_FMT " (%s) has been been marked to be excluded from killing. It is "
+ "running from the root file system, and thus likely to block re-mounting of the "
+ "root file system to read-only. Please consider moving it into an initrd file "
+ "system instead.", pid, strna(comm));
+ return true;
+ } else if (c == '@')
return true;
return false;
@@ -171,7 +186,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (parse_pid(d->d_name, &pid) < 0)
continue;
- if (ignore_proc(pid))
+ if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
continue;
if (sig == SIGKILL) {
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 3fa66f91aa..569632e13b 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -65,6 +65,7 @@ int unit_load_dropin(Unit *u) {
}
}
+ u->dropin_paths = strv_free(u->dropin_paths);
r = unit_find_dropin_paths(u, &u->dropin_paths);
if (r <= 0)
return 0;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 0408b9a829..29ab1b6b9e 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -47,7 +47,8 @@ $1.SyslogLevel, config_parse_log_level, 0,
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
-$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
+$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
+$1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
m4_ifdef(`HAVE_SECCOMP',
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index b961916ee0..903e6f0cf6 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -38,6 +38,7 @@
#include "bus-internal.h"
#include "bus-util.h"
#include "cap-list.h"
+#include "capability-util.h"
#include "cgroup.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
@@ -1033,7 +1034,7 @@ int config_parse_exec_secure_bits(const char *unit,
return 0;
}
-int config_parse_bounding_set(
+int config_parse_capability_set(
const char *unit,
const char *filename,
unsigned line,
@@ -1045,8 +1046,8 @@ int config_parse_bounding_set(
void *data,
void *userdata) {
- uint64_t *capability_bounding_set_drop = data;
- uint64_t capability_bounding_set, sum = 0;
+ uint64_t *capability_set = data;
+ uint64_t sum = 0, initial = 0;
bool invert = false;
const char *p;
@@ -1060,10 +1061,9 @@ int config_parse_bounding_set(
rvalue++;
}
- /* Note that we store this inverted internally, since the
- * kernel wants it like this. But we actually expose it
- * non-inverted everywhere to have a fully normalized
- * interface. */
+ if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
+ initial = CAP_ALL; /* initialized to all bits on */
+ /* else "AmbientCapabilities" initialized to all bits off */
p = rvalue;
for (;;) {
@@ -1082,18 +1082,21 @@ int config_parse_bounding_set(
cap = capability_from_name(word);
if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
continue;
}
sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
}
- capability_bounding_set = invert ? ~sum : sum;
- if (*capability_bounding_set_drop != 0 && capability_bounding_set != 0)
- *capability_bounding_set_drop = ~(~*capability_bounding_set_drop | capability_bounding_set);
+ sum = invert ? ~sum : sum;
+
+ if (sum == 0 || *capability_set == initial)
+ /* "" or uninitialized data -> replace */
+ *capability_set = sum;
else
- *capability_bounding_set_drop = ~capability_bounding_set;
+ /* previous data -> merge */
+ *capability_set |= sum;
return 0;
}
@@ -4011,7 +4014,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_log_level, "LEVEL" },
{ config_parse_exec_capabilities, "CAPABILITIES" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
- { config_parse_bounding_set, "BOUNDINGSET" },
+ { config_parse_capability_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index a451fc164a..f0027a6b43 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -56,7 +56,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns
int config_parse_exec_cpu_affinity(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_capabilities(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_secure_bits(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_bounding_set(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_capability_set(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_limit(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_bytes_limit(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_sec_limit(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);
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 145ba2a28d..09b0449c80 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -198,7 +198,7 @@ static int generate_machine_id(char id[34], const char *root) {
return 0;
}
-int machine_id_setup(const char *root) {
+int machine_id_setup(const char *root, sd_id128_t machine_id) {
const char *etc_machine_id, *run_machine_id;
_cleanup_close_ int fd = -1;
bool writable = true;
@@ -248,15 +248,22 @@ int machine_id_setup(const char *root) {
}
}
- if (read_machine_id(fd, id) >= 0)
- return 0;
+ /* A machine id argument overrides all other machined-ids */
+ if (!sd_id128_is_null(machine_id)) {
+ sd_id128_to_string(machine_id, id);
+ id[32] = '\n';
+ id[33] = 0;
+ } else {
+ if (read_machine_id(fd, id) >= 0)
+ return 0;
- /* Hmm, so, the id currently stored is not useful, then let's
- * generate one */
+ /* Hmm, so, the id currently stored is not useful, then let's
+ * generate one */
- r = generate_machine_id(id, root);
- if (r < 0)
- return r;
+ r = generate_machine_id(id, root);
+ if (r < 0)
+ return r;
+ }
if (writable)
if (write_machine_id(fd, id) >= 0)
diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h
index f7707c3bf9..a2168a8d4a 100644
--- a/src/core/machine-id-setup.h
+++ b/src/core/machine-id-setup.h
@@ -22,4 +22,4 @@
***/
int machine_id_commit(const char *root);
-int machine_id_setup(const char *root);
+int machine_id_setup(const char *root, sd_id128_t machine_id);
diff --git a/src/core/main.c b/src/core/main.c
index f9de54028e..27ba6af031 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -117,7 +117,7 @@ static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
-static uint64_t arg_capability_bounding_set_drop = 0;
+static uint64_t arg_capability_bounding_set = CAP_ALL;
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
@@ -127,6 +127,7 @@ static bool arg_default_blockio_accounting = false;
static bool arg_default_memory_accounting = false;
static bool arg_default_tasks_accounting = true;
static uint64_t arg_default_tasks_max = UINT64_C(512);
+static sd_id128_t arg_machine_id = {};
static void pager_open_if_enabled(void) {
@@ -300,6 +301,17 @@ static int parse_crash_chvt(const char *value) {
return 0;
}
+static int set_machine_id(const char *m) {
+
+ if (sd_id128_from_string(m, &arg_machine_id) < 0)
+ return -EINVAL;
+
+ if (sd_id128_is_null(arg_machine_id))
+ return -EINVAL;
+
+ return 0;
+}
+
static int parse_proc_cmdline_item(const char *key, const char *value) {
int r;
@@ -388,6 +400,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
+ } else if (streq(key, "systemd.machine_id") && value) {
+
+ r = set_machine_id(value);
+ if (r < 0)
+ log_warning("MachineID '%s' is not valid. Ignoring.", value);
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_UNSET)
@@ -644,7 +662,7 @@ static int parse_config_file(void) {
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
- { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
+ { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
#ifdef HAVE_SECCOMP
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#endif
@@ -743,7 +761,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DESERIALIZE,
ARG_SWITCHED_ROOT,
ARG_DEFAULT_STD_OUTPUT,
- ARG_DEFAULT_STD_ERROR
+ ARG_DEFAULT_STD_ERROR,
+ ARG_MACHINE_ID
};
static const struct option options[] = {
@@ -769,6 +788,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT },
{ "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
{ "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
+ { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
{}
};
@@ -964,6 +984,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_switched_root = true;
break;
+ case ARG_MACHINE_ID:
+ r = set_machine_id(optarg);
+ if (r < 0) {
+ log_error("MachineID '%s' is not valid.", optarg);
+ return r;
+ }
+ break;
+
case 'h':
arg_action = ACTION_HELP;
if (arg_no_pager < 0)
@@ -1617,7 +1645,7 @@ int main(int argc, char *argv[]) {
status_welcome();
hostname_setup();
- machine_id_setup(NULL);
+ machine_id_setup(NULL, arg_machine_id);
loopback_setup();
bump_unix_max_dgram_qlen();
@@ -1631,14 +1659,14 @@ int main(int argc, char *argv[]) {
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
log_error_errno(errno, "Failed to adjust timer slack: %m");
- if (arg_capability_bounding_set_drop) {
- r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+ if (!cap_test_all(arg_capability_bounding_set)) {
+ r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
error_message = "Failed to drop capability bounding set of usermode helpers";
goto finish;
}
- r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+ r = capability_bounding_set_drop(arg_capability_bounding_set, true);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
error_message = "Failed to drop capability bounding set";
@@ -1940,6 +1968,15 @@ finish:
(void) clearenv();
assert(i <= args_size);
+
+ /*
+ * We want valgrind to print its memory usage summary before reexecution.
+ * Valgrind won't do this is on its own on exec(), but it will do it on exit().
+ * Hence, to ensure we get a summary here, fork() off a child, let it exit() cleanly,
+ * so that it prints the summary, and wait() for it in the parent, before proceeding into the exec().
+ */
+ valgrind_summary_hack();
+
(void) execv(args[0], (char* const*) args);
}
diff --git a/src/core/manager.c b/src/core/manager.c
index 711b0cdcee..a83a8b013a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -233,7 +233,7 @@ static int have_ask_password(void) {
errno = 0;
de = readdir(dir);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
return false;
@@ -989,7 +989,7 @@ Manager* manager_free(Manager *m) {
free(m->switch_root_init);
for (i = 0; i < _RLIMIT_MAX; i++)
- free(m->rlimit[i]);
+ m->rlimit[i] = mfree(m->rlimit[i]);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
@@ -2923,6 +2923,8 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
assert(m);
for (i = 0; i < _RLIMIT_MAX; i++) {
+ m->rlimit[i] = mfree(m->rlimit[i]);
+
if (!default_rlimit[i])
continue;
diff --git a/src/core/service.c b/src/core/service.c
index c5b689a35c..ae84cccbc8 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -3201,7 +3201,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
if (s->state != SERVICE_DEAD)
return -EAGAIN;
- if (getpeername_pretty(fd, &peer) >= 0) {
+ if (getpeername_pretty(fd, true, &peer) >= 0) {
if (UNIT(s)->description) {
_cleanup_free_ char *a;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 2f163190e9..8b0ed74643 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -27,6 +27,7 @@
#include "bus-error.h"
#include "terminal-util.h"
#include "transaction.h"
+#include "dbus-unit.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -860,30 +861,12 @@ int transaction_add_job_and_dependencies(
if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED))
return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
- if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
- if (unit->load_error == -ENOENT || unit->manager->test_run)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id,
- strerror(-unit->load_error));
- else
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s. "
- "See system logs and 'systemctl status %s' for details.",
- unit->id,
- strerror(-unit->load_error),
- unit->id);
+ if (type != JOB_STOP) {
+ r = bus_unit_check_load_state(unit, e);
+ if (r < 0)
+ return r;
}
- if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id, strerror(-unit->load_error));
-
- if (type != JOB_STOP && unit->load_state == UNIT_MASKED)
- return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED,
- "Unit %s is masked.", unit->id);
-
if (!unit_job_is_applicable(unit, type))
return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
"Job type %s is not applicable for unit %s.",
@@ -949,9 +932,10 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
+ /* unit masked and unit not found are not considered as errors. */
log_unit_full(dep,
- r == -EBADR /* unit masked */ ? LOG_DEBUG : LOG_WARNING, r,
- "Cannot add dependency job, ignoring: %s",
+ r == -EBADR || r == -ENOENT ? LOG_DEBUG : LOG_WARNING,
+ r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
sd_bus_error_free(e);
}
diff --git a/src/core/unit.c b/src/core/unit.c
index b977ac7f0c..32267d95f5 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -51,6 +51,7 @@
#include "set.h"
#include "special.h"
#include "stat-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
@@ -1412,7 +1413,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
@@ -3231,7 +3232,7 @@ int unit_patch_contexts(Unit *u) {
ec->no_new_privileges = true;
if (ec->private_devices)
- ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
+ ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
}
cc = unit_get_cgroup_context(u);
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 469ee7af68..cc5e9741fe 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -502,7 +502,7 @@ static int write_root_shadow(const char *path, const struct spwd *p) {
errno = 0;
if (putspent(p, f) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return fflush_and_check(f);
}
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 87b8b77f22..1468dc8df6 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -463,8 +463,6 @@ static int parse_fstab(bool initrd) {
"x-systemd.automount\0");
if (initrd)
post = SPECIAL_INITRD_FS_TARGET;
- else if (mount_in_initrd(me))
- post = SPECIAL_INITRD_ROOT_FS_TARGET;
else if (mount_is_network(me))
post = SPECIAL_REMOTE_FS_TARGET;
else
@@ -576,7 +574,7 @@ static int add_sysroot_usr_mount(void) {
false,
false,
false,
- SPECIAL_INITRD_ROOT_FS_TARGET,
+ SPECIAL_INITRD_FS_TARGET,
"/proc/cmdline");
}
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 03df7365b5..bddc0c441a 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -112,7 +112,7 @@ static int verify_tty(const char *name) {
errno = 0;
if (isatty(fd) <= 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c
index 82f519958c..b44dbb14ea 100644
--- a/src/import/aufs-util.c
+++ b/src/import/aufs-util.c
@@ -69,7 +69,7 @@ int aufs_resolve(const char *path) {
errno = 0;
r = nftw(path, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
if (r == FTW_STOP)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
diff --git a/src/import/import-common.c b/src/import/import-common.c
index a8551ca9e8..8a48bd7bf9 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -134,7 +134,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
- r = capability_bounding_set_drop(~retain, true);
+ r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
@@ -208,7 +208,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
if (unshare(CLONE_NEWNET) < 0)
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
- r = capability_bounding_set_drop(~retain, true);
+ r = capability_bounding_set_drop(retain, true);
if (r < 0)
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 4e96fb0a4d..f5fe165fa3 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -700,7 +700,7 @@ static int request_handler_file(
if (fstat(fd, &st) < 0)
return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
- response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0);
+ response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0);
if (!response)
return respond_oom(connection);
@@ -840,7 +840,7 @@ static int request_handler(
assert(method);
if (!streq(method, "GET"))
- return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
"Unsupported method.\n");
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 3f93e85232..cfe111fd91 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -150,7 +150,7 @@ static int spawn_curl(const char* url) {
return r;
}
-static int spawn_getter(const char *getter, const char *url) {
+static int spawn_getter(const char *getter) {
int r;
_cleanup_strv_free_ char **words = NULL;
@@ -159,10 +159,6 @@ static int spawn_getter(const char *getter, const char *url) {
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");
- r = strv_extend(&words, url);
- if (r < 0)
- return log_error_errno(r, "Failed to create command line: %m");
-
r = spawn_child(words[0], words);
if (r < 0)
log_error_errno(r, "Failed to spawn getter %s: %m", getter);
@@ -447,7 +443,7 @@ static int add_raw_socket(RemoteServer *s, int fd) {
static int setup_raw_socket(RemoteServer *s, const char *address) {
int fd;
- fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
+ fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC);
if (fd < 0)
return fd;
@@ -587,7 +583,7 @@ static int request_handler(
*connection_cls);
if (!streq(method, "POST"))
- return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+ return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE,
"Unsupported method.\n");
if (!streq(url, "/upload"))
@@ -621,7 +617,7 @@ static int request_handler(
if (r < 0)
return code;
} else {
- r = getnameinfo_pretty(fd, &hostname);
+ r = getpeername_pretty(fd, false, &hostname);
if (r < 0)
return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
"Cannot check remote hostname");
@@ -765,7 +761,7 @@ static int setup_microhttpd_socket(RemoteServer *s,
const char *trust) {
int fd;
- fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM | SOCK_CLOEXEC);
+ fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC);
if (fd < 0)
return fd;
@@ -879,7 +875,7 @@ static int remoteserver_init(RemoteServer *s,
} else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
char *hostname;
- r = getnameinfo_pretty(fd, &hostname);
+ r = getpeername_pretty(fd, false, &hostname);
if (r < 0)
return log_error_errno(r, "Failed to retrieve remote name: %m");
@@ -897,18 +893,32 @@ static int remoteserver_init(RemoteServer *s,
fd);
}
- if (arg_url) {
- const char *url, *hostname;
+ if (arg_getter) {
+ log_info("Spawning getter %s...", arg_getter);
+ fd = spawn_getter(arg_getter);
+ if (fd < 0)
+ return fd;
+
+ r = add_source(s, fd, (char*) arg_output, false);
+ if (r < 0)
+ return r;
+ }
- url = strjoina(arg_url, "/entries");
+ if (arg_url) {
+ const char *url;
+ char *hostname, *p;
- if (arg_getter) {
- log_info("Spawning getter %s...", url);
- fd = spawn_getter(arg_getter, url);
- } else {
- log_info("Spawning curl %s...", url);
- fd = spawn_curl(url);
+ if (!strstr(arg_url, "/entries")) {
+ if (endswith(arg_url, "/"))
+ url = strjoina(arg_url, "entries");
+ else
+ url = strjoina(arg_url, "/entries");
}
+ else
+ url = strdupa(arg_url);
+
+ log_info("Spawning curl %s...", url);
+ fd = spawn_curl(url);
if (fd < 0)
return fd;
@@ -917,7 +927,13 @@ static int remoteserver_init(RemoteServer *s,
startswith(arg_url, "http://") ?:
arg_url;
- r = add_source(s, fd, (char*) hostname, false);
+ hostname = strdupa(hostname);
+ if ((p = strchr(hostname, '/')))
+ *p = '\0';
+ if ((p = strchr(hostname, ':')))
+ *p = '\0';
+
+ r = add_source(s, fd, hostname, false);
if (r < 0)
return r;
}
diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c
index a6d7c3b7e8..f9d2385215 100644
--- a/src/journal-remote/journal-upload-journal.c
+++ b/src/journal-remote/journal-upload-journal.c
@@ -312,6 +312,9 @@ void close_journal_input(Uploader *u) {
static int process_journal_input(Uploader *u, int skip) {
int r;
+ if (u->uploading)
+ return 0;
+
r = sd_journal_next_skip(u->journal, skip);
if (r < 0)
return log_error_errno(r, "Failed to skip to next entry: %m");
@@ -349,10 +352,8 @@ static int dispatch_journal_input(sd_event_source *event,
assert(u);
- if (u->uploading) {
- log_warning("dispatch_journal_input called when uploading, ignoring.");
+ if (u->uploading)
return 0;
- }
log_debug("Detected journal input, checking for new data.");
return check_journal_input(u);
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index 3e8c4fa6d1..cba57403a3 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -26,6 +26,15 @@
#include "macro.h"
+/* Compatiblity with libmicrohttpd < 0.9.38 */
+#ifndef MHD_HTTP_NOT_ACCEPTABLE
+#define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE
+#endif
+
+#if MHD_VERSION < 0x00094203
+#define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset
+#endif
+
void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
/* respond_oom() must be usable with return, hence this form. */
diff --git a/src/journal/compress.c b/src/journal/compress.c
index 1828165894..78935fee74 100644
--- a/src/journal/compress.c
+++ b/src/journal/compress.c
@@ -612,80 +612,8 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
#endif
}
+int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
#ifdef HAVE_LZ4
-static int decompress_stream_lz4_v1(int fdf, int fdt, uint64_t max_bytes) {
-
- _cleanup_free_ char *buf = NULL, *out = NULL;
- size_t buf_size = 0;
- LZ4_streamDecode_t lz4_data = {};
- le32_t header;
- size_t total_in = sizeof(header), total_out = 0;
-
- assert(fdf >= 0);
- assert(fdt >= 0);
-
- out = malloc(4*LZ4_BUFSIZE);
- if (!out)
- return -ENOMEM;
-
- for (;;) {
- ssize_t m;
- int r;
-
- r = loop_read_exact(fdf, &header, sizeof(header), false);
- if (r < 0)
- return r;
-
- m = le32toh(header);
- if (m == 0)
- break;
-
- /* We refuse to use a bigger decompression buffer than
- * the one used for compression by 4 times. This means
- * that compression buffer size can be enlarged 4
- * times. This can be changed, but old binaries might
- * not accept buffers compressed by newer binaries then.
- */
- if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
- log_debug("Compressed stream block too big: %zd bytes", m);
- return -ENOBUFS;
- }
-
- total_in += sizeof(header) + m;
-
- if (!GREEDY_REALLOC(buf, buf_size, m))
- return -ENOMEM;
-
- r = loop_read_exact(fdf, buf, m, false);
- if (r < 0)
- return r;
-
- r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
- if (r <= 0) {
- log_debug("LZ4 decompression failed (legacy format).");
- return -EBADMSG;
- }
-
- total_out += r;
-
- if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) {
- log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes);
- return -EFBIG;
- }
-
- r = loop_write(fdt, out, r, false);
- if (r < 0)
- return r;
- }
-
- log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
- total_in, total_out,
- (double) total_out / total_in * 100);
-
- return 0;
-}
-
-static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
size_t c;
_cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
_cleanup_free_ char *buf = NULL;
@@ -739,17 +667,6 @@ static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
cleanup:
munmap(src, st.st_size);
return r;
-}
-#endif
-
-int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
-#ifdef HAVE_LZ4
- int r;
-
- r = decompress_stream_lz4_v2(fdf, fdt, max_bytes);
- if (r == -EBADMSG)
- r = decompress_stream_lz4_v1(fdf, fdt, max_bytes);
- return r;
#else
log_debug("Cannot decompress file. Compiled without LZ4 support.");
return -EPROTONOSUPPORT;
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index f750ddfcbd..8298b02439 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -297,7 +297,8 @@ static int save_external_coredump(
const char *info[_INFO_LEN],
uid_t uid,
char **ret_filename,
- int *ret_fd,
+ int *ret_node_fd,
+ int *ret_data_fd,
uint64_t *ret_size) {
_cleanup_free_ char *fn = NULL, *tmp = NULL;
@@ -307,7 +308,8 @@ static int save_external_coredump(
assert(info);
assert(ret_filename);
- assert(ret_fd);
+ assert(ret_node_fd);
+ assert(ret_data_fd);
assert(ret_size);
r = make_filename(info, &fn);
@@ -386,11 +388,12 @@ static int save_external_coredump(
unlink_noerrno(tmp);
*ret_filename = fn_compressed; /* compressed */
- *ret_fd = fd; /* uncompressed */
+ *ret_node_fd = fd_compressed; /* compressed */
+ *ret_data_fd = fd; /* uncompressed */
*ret_size = (uint64_t) st.st_size; /* uncompressed */
fn_compressed = NULL;
- fd = -1;
+ fd = fd_compressed = -1;
return 0;
@@ -400,12 +403,14 @@ static int save_external_coredump(
uncompressed:
#endif
+
r = fix_permissions(fd, tmp, fn, info, uid);
if (r < 0)
goto fail;
*ret_filename = fn;
- *ret_fd = fd;
+ *ret_data_fd = fd;
+ *ret_node_fd = -1;
*ret_size = (uint64_t) st.st_size;
fn = NULL;
@@ -527,7 +532,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
errno = 0;
stream = safe_fclose(stream);
- if (errno != 0)
+ if (errno > 0)
return -errno;
*open_fds = buffer;
@@ -554,7 +559,7 @@ int main(int argc, char* argv[]) {
_cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
const char *info[_INFO_LEN];
- _cleanup_close_ int coredump_fd = -1;
+ _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
struct iovec iovec[26];
uint64_t coredump_size;
@@ -633,7 +638,7 @@ int main(int argc, char* argv[]) {
if (arg_storage != COREDUMP_STORAGE_NONE)
arg_storage = COREDUMP_STORAGE_EXTERNAL;
- r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
+ r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
if (r < 0)
goto finish;
@@ -789,13 +794,15 @@ int main(int argc, char* argv[]) {
IOVEC_SET_STRING(iovec[j++], core_timestamp);
IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+
+ assert_cc(2 == LOG_CRIT);
IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
/* Vacuum before we write anything again */
coredump_vacuum(-1, arg_keep_free, arg_max_use);
/* Always stream the coredump to disk, if that's possible */
- r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
+ r = save_external_coredump(info, uid, &filename, &coredump_node_fd, &coredump_fd, &coredump_size);
if (r < 0)
/* skip whole core dumping part */
goto log;
@@ -815,7 +822,7 @@ int main(int argc, char* argv[]) {
}
/* Vacuum again, but exclude the coredump we just created */
- coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
+ coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
/* Now, let's drop privileges to become the user who owns the
* segfaulted process and allocate the coredump memory under
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 9e362bacae..2973176c8d 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -39,6 +39,7 @@
#include "lookup3.h"
#include "parse-util.h"
#include "random-util.h"
+#include "sd-event.h"
#include "string-util.h"
#include "xattr-util.h"
@@ -149,6 +150,17 @@ JournalFile* journal_file_close(JournalFile *f) {
journal_file_append_tag(f);
#endif
+ if (f->post_change_timer) {
+ int enabled;
+
+ if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0)
+ if (enabled == SD_EVENT_ONESHOT)
+ journal_file_post_change(f);
+
+ (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF);
+ sd_event_source_unref(f->post_change_timer);
+ }
+
journal_file_set_offline(f);
if (f->mmap && f->fd >= 0)
@@ -1397,7 +1409,84 @@ void journal_file_post_change(JournalFile *f) {
__sync_synchronize();
if (ftruncate(f->fd, f->last_stat.st_size) < 0)
- log_error_errno(errno, "Failed to truncate file to its own size: %m");
+ log_debug_errno(errno, "Failed to truncate file to its own size: %m");
+}
+
+static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) {
+ assert(userdata);
+
+ journal_file_post_change(userdata);
+
+ return 1;
+}
+
+static void schedule_post_change(JournalFile *f) {
+ sd_event_source *timer;
+ int enabled, r;
+ uint64_t now;
+
+ assert(f);
+ assert(f->post_change_timer);
+
+ timer = f->post_change_timer;
+
+ r = sd_event_source_get_enabled(timer, &enabled);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get ftruncate timer state: %m");
+ goto fail;
+ }
+
+ if (enabled == SD_EVENT_ONESHOT)
+ return;
+
+ r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m");
+ goto fail;
+ }
+
+ r = sd_event_source_set_time(timer, now+f->post_change_timer_period);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m");
+ goto fail;
+ }
+
+ r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to enable scheduled ftruncate: %m");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ /* On failure, let's simply post the change immediately. */
+ journal_file_post_change(f);
+}
+
+/* Enable coalesced change posting in a timer on the provided sd_event instance */
+int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
+ int r;
+
+ assert(f);
+ assert_return(!f->post_change_timer, -EINVAL);
+ assert(e);
+ assert(t);
+
+ r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(timer, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
+ f->post_change_timer = timer;
+ timer = NULL;
+ f->post_change_timer_period = t;
+
+ return r;
}
static int entry_item_cmp(const void *_a, const void *_b) {
@@ -1465,7 +1554,10 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st
if (mmap_cache_got_sigbus(f->mmap, f->fd))
r = -EIO;
- journal_file_post_change(f);
+ if (f->post_change_timer)
+ schedule_post_change(f);
+ else
+ journal_file_post_change(f);
return r;
}
@@ -2767,6 +2859,16 @@ int journal_file_open(
goto fail;
}
+ if (template && template->post_change_timer) {
+ r = journal_file_enable_post_change_timer(
+ f,
+ sd_event_source_get_event(template->post_change_timer),
+ template->post_change_timer_period);
+
+ if (r < 0)
+ goto fail;
+ }
+
*ret = f;
return 0;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 46c1f3278e..7970ebe738 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -33,6 +33,7 @@
#include "journal-def.h"
#include "macro.h"
#include "mmap-cache.h"
+#include "sd-event.h"
#include "sparse-endian.h"
typedef struct JournalMetrics {
@@ -101,6 +102,9 @@ typedef struct JournalFile {
JournalMetrics metrics;
MMapCache *mmap;
+ sd_event_source *post_change_timer;
+ usec_t post_change_timer_period;
+
OrderedHashmap *chain_cache;
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
@@ -224,6 +228,7 @@ void journal_file_print_header(JournalFile *f);
int journal_file_rotate(JournalFile **f, bool compress, bool seal);
void journal_file_post_change(JournalFile *f);
+int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t);
void journal_reset_metrics(JournalMetrics *m);
void journal_default_metrics(JournalMetrics *m, int fd);
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index 44fa11a00e..def4caab92 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -372,6 +372,7 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove
xsprintf(error, "ERRNO=%i", _saved_errno_);
+ assert_cc(3 == LOG_ERR);
IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
IOVEC_SET_STRING(iov[skip+1], buffer);
IOVEC_SET_STRING(iov[skip+2], error);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index d009b2e93b..db11421e7a 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -2336,7 +2336,7 @@ int main(int argc, char *argv[]) {
flags =
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
arg_catalog * OUTPUT_CATALOG |
arg_utc * OUTPUT_UTC;
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index 3c13fe0d67..28970131e7 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -397,8 +397,8 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
IOVEC_SET_STRING(iov[n_iov++], id_field);
- assert_cc(32 == LOG_AUTH);
- IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=32");
+ assert_cc(4 == LOG_FAC(LOG_AUTH));
+ IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=4");
IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit");
type_name = audit_type_name_alloca(type);
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index e048e04716..1306ad6974 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -158,8 +158,10 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
/* Did we lose any? */
if (serial > *s->kernel_seqnum)
- server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %"PRIu64" kernel messages",
- serial - *s->kernel_seqnum);
+ server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED,
+ LOG_MESSAGE("Missed %"PRIu64" kernel messages",
+ serial - *s->kernel_seqnum),
+ NULL);
/* Make sure we never read this one again. Note that
* we always store the next message serial we expect
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index a8a9b72080..8ff7ef943b 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -67,9 +67,11 @@
#include "selinux-util.h"
#include "signal-util.h"
#include "socket-util.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "user-util.h"
+#include "log.h"
#define USER_JOURNALS_MAX 1024
@@ -82,6 +84,9 @@
#define NOTIFY_SNDBUF_SIZE (8*1024*1024)
+/* The period to insert between posting changes for coalescing */
+#define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC)
+
static int determine_space_for(
Server *s,
JournalMetrics *metrics,
@@ -142,7 +147,7 @@ static int determine_space_for(
sum += (uint64_t) st.st_blocks * 512UL;
}
- /* If request, then let's bump the min_use limit to the
+ /* If requested, then let's bump the min_use limit to the
* current usage on disk. We do this when starting up and
* first opening the journal files. This way sudden spikes in
* disk usage will not cause journald to vacuum files without
@@ -162,19 +167,31 @@ static int determine_space_for(
if (verbose) {
char fb1[FORMAT_BYTES_MAX], fb2[FORMAT_BYTES_MAX], fb3[FORMAT_BYTES_MAX],
fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX], fb6[FORMAT_BYTES_MAX];
+ format_bytes(fb1, sizeof(fb1), sum);
+ format_bytes(fb2, sizeof(fb2), metrics->max_use);
+ format_bytes(fb3, sizeof(fb3), metrics->keep_free);
+ format_bytes(fb4, sizeof(fb4), ss_avail);
+ format_bytes(fb5, sizeof(fb5), s->cached_space_limit);
+ format_bytes(fb6, sizeof(fb6), s->cached_space_available);
server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
- "%s (%s) is currently using %s.\n"
- "Maximum allowed usage is set to %s.\n"
- "Leaving at least %s free (of currently available %s of space).\n"
- "Enforced usage limit is thus %s, of which %s are still available.",
- name, path,
- format_bytes(fb1, sizeof(fb1), sum),
- format_bytes(fb2, sizeof(fb2), metrics->max_use),
- format_bytes(fb3, sizeof(fb3), metrics->keep_free),
- format_bytes(fb4, sizeof(fb4), ss_avail),
- format_bytes(fb5, sizeof(fb5), s->cached_space_limit),
- format_bytes(fb6, sizeof(fb6), s->cached_space_available));
+ LOG_MESSAGE("%s (%s) is %s, max %s, %s free.",
+ name, path, fb1, fb5, fb6),
+ "JOURNAL_NAME=%s", name,
+ "JOURNAL_PATH=%s", path,
+ "CURRENT_USE=%"PRIu64, sum,
+ "CURRENT_USE_PRETTY=%s", fb1,
+ "MAX_USE=%"PRIu64, metrics->max_use,
+ "MAX_USE_PRETTY=%s", fb2,
+ "DISK_KEEP_FREE=%"PRIu64, metrics->keep_free,
+ "DISK_KEEP_FREE_PRETTY=%s", fb3,
+ "DISK_AVAILABLE=%"PRIu64, ss_avail,
+ "DISK_AVAILABLE_PRETTY=%s", fb4,
+ "LIMIT=%"PRIu64, s->cached_space_limit,
+ "LIMIT_PRETTY=%s", fb5,
+ "AVAILABLE=%"PRIu64, s->cached_space_available,
+ "AVAILABLE_PRETTY=%s", fb6,
+ NULL);
}
if (available)
@@ -220,6 +237,39 @@ static void server_add_acls(JournalFile *f, uid_t uid) {
#endif
}
+static int open_journal(
+ Server *s,
+ bool reliably,
+ const char *fname,
+ int flags,
+ bool seal,
+ JournalMetrics *metrics,
+ JournalFile *template,
+ JournalFile **ret) {
+ int r;
+ JournalFile *f;
+
+ assert(s);
+ assert(fname);
+ assert(ret);
+
+ if (reliably)
+ r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, template, &f);
+ else
+ r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, template, &f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC);
+ if (r < 0) {
+ journal_file_close(f);
+ return r;
+ }
+
+ *ret = f;
+ return r;
+}
+
static JournalFile* find_journal(Server *s, uid_t uid) {
_cleanup_free_ char *p = NULL;
int r;
@@ -258,7 +308,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
journal_file_close(f);
}
- r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &f);
+ r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_metrics, NULL, &f);
if (r < 0)
return s->system_journal;
@@ -808,37 +858,56 @@ static void dispatch_message_real(
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
char mid[11 + 32 + 1];
- char buffer[16 + LINE_MAX + 1];
- struct iovec iovec[N_IOVEC_META_FIELDS + 6];
- int n = 0;
+ struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS];
+ unsigned n = 0, m;
+ int r;
va_list ap;
struct ucred ucred = {};
assert(s);
assert(format);
+ assert_cc(3 == LOG_FAC(LOG_DAEMON));
IOVEC_SET_STRING(iovec[n++], "SYSLOG_FACILITY=3");
IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=systemd-journald");
- IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver");
-
- memcpy(buffer, "MESSAGE=", 8);
- va_start(ap, format);
- vsnprintf(buffer + 8, sizeof(buffer) - 8, format, ap);
- va_end(ap);
- IOVEC_SET_STRING(iovec[n++], buffer);
+ assert_cc(6 == LOG_INFO);
+ IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
if (!sd_id128_equal(message_id, SD_ID128_NULL)) {
snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id));
IOVEC_SET_STRING(iovec[n++], mid);
}
+ m = n;
+
+ va_start(ap, format);
+ r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, false, 0, format, ap);
+ /* Error handling below */
+ va_end(ap);
+
ucred.pid = getpid();
ucred.uid = getuid();
ucred.gid = getgid();
- dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0);
+ if (r >= 0)
+ dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0);
+
+ while (m < n)
+ free(iovec[m++].iov_base);
+
+ if (r < 0) {
+ /* We failed to format the message. Emit a warning instead. */
+ char buf[LINE_MAX];
+
+ xsprintf(buf, "MESSAGE=Entry printing failed: %s", strerror(-r));
+
+ n = 3;
+ IOVEC_SET_STRING(iovec[n++], "PRIORITY=4");
+ IOVEC_SET_STRING(iovec[n++], buf);
+ dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0);
+ }
}
void server_dispatch_message(
@@ -901,7 +970,8 @@ void server_dispatch_message(
/* Write a suppression message if we suppressed something */
if (rl > 1)
server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED,
- "Suppressed %u messages from %s", rl - 1, path);
+ LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path),
+ NULL);
finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
@@ -930,7 +1000,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
(void) mkdir(fn, 0755);
fn = strjoina(fn, "/system.journal");
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, NULL, &s->system_journal);
if (r >= 0) {
server_add_acls(s->system_journal, 0);
(void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
@@ -953,7 +1023,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
* if it already exists, so that we can flush
* it into the system journal */
- r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, NULL, &s->runtime_journal);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Failed to open runtime journal: %m");
@@ -970,7 +1040,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
(void) mkdir("/run/log/journal", 0755);
(void) mkdir_parents(fn, 0750);
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, NULL, &s->runtime_journal);
if (r < 0)
return log_error_errno(r, "Failed to open runtime journal: %m");
}
@@ -1073,7 +1143,11 @@ finish:
sd_journal_close(j);
- server_driver_message(s, SD_ID128_NULL, "Time spent on flushing to /var is %s for %u entries.", format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), n);
+ server_driver_message(s, SD_ID128_NULL,
+ LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.",
+ format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0),
+ n),
+ NULL);
return r;
}
@@ -1330,7 +1404,7 @@ static int server_parse_proc_cmdline(Server *s) {
p = line;
for(;;) {
- _cleanup_free_ char *word;
+ _cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 1822765228..49bbee0646 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -157,9 +157,10 @@ struct Server {
#define N_IOVEC_KERNEL_FIELDS 64
#define N_IOVEC_UDEV_FIELDS 32
#define N_IOVEC_OBJECT_FIELDS 12
+#define N_IOVEC_PAYLOAD_FIELDS 15
void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid);
-void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,4);
+void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_;
/* gperf lookup function */
const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length);
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 2f7a805f14..9f2ccdcc77 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -326,7 +326,7 @@ void server_process_syslog_message(
size_t label_len) {
char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
- syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
+ syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
struct iovec iovec[N_IOVEC_META_FIELDS + 6];
unsigned n = 0;
@@ -357,11 +357,11 @@ void server_process_syslog_message(
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
- sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
+ xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
IOVEC_SET_STRING(iovec[n++], syslog_priority);
if (priority & LOG_FACMASK) {
- sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
+ xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
IOVEC_SET_STRING(iovec[n++], syslog_facility);
}
@@ -448,7 +448,10 @@ void server_maybe_warn_forward_syslog_missed(Server *s) {
if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
return;
- server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
+ server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED,
+ LOG_MESSAGE("Forwarding to syslog missed %u messages.",
+ s->n_forward_syslog_missed),
+ NULL);
s->n_forward_syslog_missed = 0;
s->last_warn_forward_syslog_missed = n;
diff --git a/src/journal/journald.c b/src/journal/journald.c
index b9f5c099e1..293b788d03 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -58,7 +58,9 @@ int main(int argc, char *argv[]) {
server_flush_dev_kmsg(&server);
log_debug("systemd-journald running as pid "PID_FMT, getpid());
- server_driver_message(&server, SD_MESSAGE_JOURNAL_START, "Journal started");
+ server_driver_message(&server, SD_MESSAGE_JOURNAL_START,
+ LOG_MESSAGE("Journal started"),
+ NULL);
for (;;) {
usec_t t = USEC_INFINITY, n;
@@ -109,7 +111,9 @@ int main(int argc, char *argv[]) {
}
log_debug("systemd-journald stopped as pid "PID_FMT, getpid());
- server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, "Journal stopped");
+ server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP,
+ LOG_MESSAGE("Journal stopped"),
+ NULL);
finish:
server_done(&server);
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index 1de7f3639c..9f0d96e57d 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -34,14 +34,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
assert(options);
assert(offset);
- if (code != DHCP_OPTION_END)
+ if (code != SD_DHCP_OPTION_END)
/* always make sure there is space for an END option */
size --;
switch (code) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_END:
if (size < *offset + 1)
return -ENOBUFS;
@@ -91,7 +91,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
else if (r == -ENOBUFS && (file || sname)) {
/* did not fit, but we have more buffers to try
close the options array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -112,7 +112,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
} else if (r == -ENOBUFS && sname) {
/* did not fit, but we have more buffers to try
close the file array and move the offset to its end */
- r = option_append(message->options, size, offset, DHCP_OPTION_END, 0, NULL);
+ r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -152,10 +152,10 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
code = options[offset ++];
switch (code) {
- case DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_PAD:
continue;
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_END:
return 0;
}
@@ -170,7 +170,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
option = &options[offset];
switch (code) {
- case DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
if (len != 1)
return -EINVAL;
@@ -179,7 +179,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
break;
- case DHCP_OPTION_ERROR_MESSAGE:
+ case SD_DHCP_OPTION_ERROR_MESSAGE:
if (len == 0)
return -EINVAL;
@@ -203,7 +203,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
}
break;
- case DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_OVERLOAD:
if (len != 1)
return -EINVAL;
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index 9ff42b155e..8d75d49691 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -44,7 +44,7 @@ int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
message->magic = htobe32(DHCP_MAGIC_COOKIE);
r = dhcp_option_append(message, optlen, &offset, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, &type);
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index f65529a00e..18490def06 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -105,48 +105,6 @@ enum {
DHCP_OVERLOAD_SNAME = 2,
};
-enum {
- DHCP_OPTION_PAD = 0,
- DHCP_OPTION_SUBNET_MASK = 1,
- DHCP_OPTION_TIME_OFFSET = 2,
- DHCP_OPTION_ROUTER = 3,
- DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
- DHCP_OPTION_HOST_NAME = 12,
- DHCP_OPTION_BOOT_FILE_SIZE = 13,
- DHCP_OPTION_DOMAIN_NAME = 15,
- DHCP_OPTION_ROOT_PATH = 17,
- DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
- DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
- DHCP_OPTION_POLICY_FILTER = 21,
- DHCP_OPTION_INTERFACE_MDR = 22,
- DHCP_OPTION_INTERFACE_TTL = 23,
- DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
- DHCP_OPTION_INTERFACE_MTU = 26,
- DHCP_OPTION_BROADCAST = 28,
- DHCP_OPTION_STATIC_ROUTE = 33,
- DHCP_OPTION_NTP_SERVER = 42,
- DHCP_OPTION_VENDOR_SPECIFIC = 43,
- DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
- DHCP_OPTION_OVERLOAD = 52,
- DHCP_OPTION_MESSAGE_TYPE = 53,
- DHCP_OPTION_SERVER_IDENTIFIER = 54,
- DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
- DHCP_OPTION_ERROR_MESSAGE = 56,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
- DHCP_OPTION_RENEWAL_T1_TIME = 58,
- DHCP_OPTION_REBINDING_T2_TIME = 59,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
- DHCP_OPTION_CLIENT_IDENTIFIER = 61,
- DHCP_OPTION_FQDN = 81,
- DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
- DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
- DHCP_OPTION_PRIVATE_BASE = 224,
- DHCP_OPTION_PRIVATE_LAST = 254,
- DHCP_OPTION_END = 255,
-};
-
#define DHCP_MAX_FQDN_LENGTH 255
enum {
diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c
index 850212aea1..6050851858 100644
--- a/src/libsystemd-network/dhcp6-option.c
+++ b/src/libsystemd-network/dhcp6-option.c
@@ -23,6 +23,8 @@
#include <netinet/in.h>
#include <string.h>
+#include "sd-dhcp6-client.h"
+
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-protocol.h"
@@ -90,11 +92,11 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
assert_return(buf && *buf && buflen && ia, -EINVAL);
switch (ia->type) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
break;
@@ -117,7 +119,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
*buflen -= len;
LIST_FOREACH(addresses, addr, ia->addresses) {
- r = option_append_hdr(buf, buflen, DHCP6_OPTION_IAADDR,
+ r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
sizeof(addr->iaaddr));
if (r < 0)
return r;
@@ -196,7 +198,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
assert_return(!ia->addresses, -EINVAL);
switch (iatype) {
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
@@ -219,7 +221,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_IA_TA:
+ case SD_DHCP6_OPTION_IA_TA:
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
sizeof(addr->iaaddr)) {
r = -ENOBUFS;
@@ -247,7 +249,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
switch (opt) {
- case DHCP6_OPTION_IAADDR:
+ case SD_DHCP6_OPTION_IAADDR:
addr = new0(DHCP6Address, 1);
if (!addr) {
@@ -274,7 +276,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < sizeof(status))
break;
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index b3a28f88b4..246cc94cd8 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -99,41 +99,6 @@ enum {
};
enum {
- DHCP6_OPTION_CLIENTID = 1,
- DHCP6_OPTION_SERVERID = 2,
- DHCP6_OPTION_IA_NA = 3,
- DHCP6_OPTION_IA_TA = 4,
- DHCP6_OPTION_IAADDR = 5,
- DHCP6_OPTION_ORO = 6,
- DHCP6_OPTION_PREFERENCE = 7,
- DHCP6_OPTION_ELAPSED_TIME = 8,
- DHCP6_OPTION_RELAY_MSG = 9,
- /* option code 10 is unassigned */
- DHCP6_OPTION_AUTH = 11,
- DHCP6_OPTION_UNICAST = 12,
- DHCP6_OPTION_STATUS_CODE = 13,
- DHCP6_OPTION_RAPID_COMMIT = 14,
- DHCP6_OPTION_USER_CLASS = 15,
- DHCP6_OPTION_VENDOR_CLASS = 16,
- DHCP6_OPTION_VENDOR_OPTS = 17,
- DHCP6_OPTION_INTERFACE_ID = 18,
- DHCP6_OPTION_RECONF_MSG = 19,
- DHCP6_OPTION_RECONF_ACCEPT = 20,
-
- DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
- DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
-
- DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
-
- /* option code 35 is unassigned */
-
- DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
-
- /* option codes 89-142 are unassigned */
- /* option codes 144-65535 are unassigned */
-};
-
-enum {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index a4d4f1ae2f..5da06435ed 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -437,7 +437,7 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
return size;
}
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
unsigned i;
assert(f);
@@ -448,10 +448,15 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route
fprintf(f, "%s=", key);
for (i = 0; i < size; i++) {
- fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr),
- routes[i].dst_prefixlen);
- fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr),
- (i < (size - 1)) ? " ": "");
+ struct in_addr dest, gw;
+ uint8_t length;
+
+ assert_se(sd_dhcp_route_get_destination(routes[i], &dest) >= 0);
+ assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
+
+ fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length);
+ fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": "");
}
fputs("\n", f);
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index 8a30921966..c43c01accf 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -23,6 +23,8 @@
#include <stdbool.h>
+#include "sd-dhcp-lease.h"
+
#include "condition.h"
#include "udev.h"
@@ -74,7 +76,7 @@ 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 */
struct sd_dhcp_route;
-void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
+void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 4521f8f0b1..d3ad6b7717 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -104,11 +104,11 @@ struct sd_dhcp_client {
};
static const uint8_t default_req_opts[] = {
- DHCP_OPTION_SUBNET_MASK,
- DHCP_OPTION_ROUTER,
- DHCP_OPTION_HOST_NAME,
- DHCP_OPTION_DOMAIN_NAME,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
+ SD_DHCP_OPTION_SUBNET_MASK,
+ SD_DHCP_OPTION_ROUTER,
+ SD_DHCP_OPTION_HOST_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
};
static int client_receive_message_raw(sd_event_source *s, int fd,
@@ -143,11 +143,11 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
DHCP_STATE_STOPPED), -EBUSY);
switch(option) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_OVERLOAD:
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_PARAMETER_REQUEST_LIST:
- case DHCP_OPTION_END:
+ case SD_DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
+ case SD_DHCP_OPTION_END:
return -EINVAL;
default:
@@ -486,7 +486,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
Identifier option is not set */
if (client->client_id_len) {
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_CLIENT_IDENTIFIER,
+ SD_DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id_len,
&client->client_id);
if (r < 0)
@@ -502,7 +502,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
messages.
*/
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_PARAMETER_REQUEST_LIST,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
if (r < 0)
return r;
@@ -531,7 +531,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
*/
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
@@ -557,7 +557,7 @@ static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t
r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
if (r > 0)
r = dhcp_option_append(message, optlen, optoffset, 0,
- DHCP_OPTION_FQDN, 3 + r, buffer);
+ SD_DHCP_OPTION_FQDN, 3 + r, buffer);
return r;
}
@@ -593,7 +593,7 @@ static int client_send_discover(sd_dhcp_client *client) {
*/
if (client->last_addr != INADDR_ANY) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
@@ -609,7 +609,7 @@ static int client_send_discover(sd_dhcp_client *client) {
DHCPDISCOVER but dhclient does and so we do as well
*/
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
+ SD_DHCP_OPTION_HOST_NAME,
strlen(client->hostname), client->hostname);
} else
r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
@@ -620,7 +620,7 @@ static int client_send_discover(sd_dhcp_client *client) {
if (client->vendor_class_identifier) {
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
strlen(client->vendor_class_identifier),
client->vendor_class_identifier);
if (r < 0)
@@ -628,7 +628,7 @@ static int client_send_discover(sd_dhcp_client *client) {
}
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -667,13 +667,13 @@ static int client_send_request(sd_dhcp_client *client) {
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &client->lease->server_address);
if (r < 0)
return r;
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->lease->address);
if (r < 0)
return r;
@@ -686,7 +686,7 @@ static int client_send_request(sd_dhcp_client *client) {
assigned address. ’ciaddr’ MUST be zero.
*/
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return r;
@@ -721,7 +721,7 @@ static int client_send_request(sd_dhcp_client *client) {
if (client->hostname) {
if (dns_name_is_single_label(client->hostname))
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
+ SD_DHCP_OPTION_HOST_NAME,
strlen(client->hostname), client->hostname);
else
r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
@@ -731,7 +731,7 @@ static int client_send_request(sd_dhcp_client *client) {
}
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index e875ba4986..7a119fd488 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -37,6 +37,7 @@
#include "in-addr-util.h"
#include "network-internal.h"
#include "parse-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "unaligned.h"
@@ -205,14 +206,28 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
+/*
+ * The returned routes array must be freed by the caller.
+ * Route objects have the same lifetime of the lease and must not be freed.
+ */
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
+ sd_dhcp_route **ret;
+ unsigned i;
+
assert_return(lease, -EINVAL);
assert_return(routes, -EINVAL);
if (lease->static_route_size <= 0)
return -ENODATA;
- *routes = lease->static_route;
+ ret = new(sd_dhcp_route *, lease->static_route_size);
+ if (!ret)
+ return -ENOMEM;
+
+ for (i = 0; i < lease->static_route_size; i++)
+ ret[i] = &lease->static_route[i];
+
+ *routes = ret;
return (int) lease->static_route_size;
}
@@ -338,6 +353,38 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
return 0;
}
+static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
+ _cleanup_free_ char *name = NULL, *normalized = NULL;
+ int r;
+
+ assert(option);
+ assert(ret);
+
+ r = lease_parse_string(option, len, &name);
+ if (r < 0)
+ return r;
+ if (!name) {
+ *ret = mfree(*ret);
+ return 0;
+ }
+
+ r = dns_name_normalize(name, &normalized);
+ if (r < 0)
+ return r;
+
+ if (is_localhost(normalized))
+ return -EINVAL;
+
+ if (dns_name_is_root(normalized))
+ return -EINVAL;
+
+ free(*ret);
+ *ret = normalized;
+ normalized = NULL;
+
+ return 0;
+}
+
static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
assert(option);
assert(ret);
@@ -452,7 +499,7 @@ static int lease_parse_classless_routes(
if (len < 4)
return -EINVAL;
- lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+ assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
option += 4;
len -= 4;
@@ -470,21 +517,21 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
switch(code) {
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
r = lease_parse_u32(option, len, &lease->lifetime, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse lease time, ignoring: %m");
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
r = lease_parse_be32(option, len, &lease->server_address);
if (r < 0)
log_debug_errno(r, "Failed to parse server identifier, ignoring: %m");
break;
- case DHCP_OPTION_SUBNET_MASK:
+ case SD_DHCP_OPTION_SUBNET_MASK:
r = lease_parse_be32(option, len, &lease->subnet_mask);
if (r < 0)
log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m");
@@ -492,7 +539,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->have_subnet_mask = true;
break;
- case DHCP_OPTION_BROADCAST:
+ case SD_DHCP_OPTION_BROADCAST:
r = lease_parse_be32(option, len, &lease->broadcast);
if (r < 0)
log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m");
@@ -500,7 +547,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->have_broadcast = true;
break;
- case DHCP_OPTION_ROUTER:
+ case SD_DHCP_OPTION_ROUTER:
if (len >= 4) {
r = lease_parse_be32(option, 4, &lease->router);
if (r < 0)
@@ -508,103 +555,67 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
}
break;
- case DHCP_OPTION_DOMAIN_NAME_SERVER:
+ case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
if (r < 0)
log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
break;
- case DHCP_OPTION_NTP_SERVER:
+ case SD_DHCP_OPTION_NTP_SERVER:
r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
if (r < 0)
log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
break;
- case DHCP_OPTION_STATIC_ROUTE:
+ case SD_DHCP_OPTION_STATIC_ROUTE:
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
if (r < 0)
log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
break;
- case DHCP_OPTION_INTERFACE_MTU:
+ case SD_DHCP_OPTION_INTERFACE_MTU:
r = lease_parse_u16(option, len, &lease->mtu, 68);
if (r < 0)
log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
break;
- case DHCP_OPTION_DOMAIN_NAME: {
- _cleanup_free_ char *domainname = NULL, *normalized = NULL;
-
- r = lease_parse_string(option, len, &domainname);
+ case SD_DHCP_OPTION_DOMAIN_NAME:
+ r = lease_parse_domain(option, len, &lease->domainname);
if (r < 0) {
log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
return 0;
}
- r = dns_name_normalize(domainname, &normalized);
- if (r < 0) {
- log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
- return 0;
- }
-
- if (is_localhost(normalized)) {
- log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring.");
- break;
- }
-
- free(lease->domainname);
- lease->domainname = normalized;
- normalized = NULL;
-
break;
- }
-
- case DHCP_OPTION_HOST_NAME: {
- _cleanup_free_ char *hostname = NULL, *normalized = NULL;
- r = lease_parse_string(option, len, &hostname);
+ case SD_DHCP_OPTION_HOST_NAME:
+ r = lease_parse_domain(option, len, &lease->hostname);
if (r < 0) {
log_debug_errno(r, "Failed to parse host name, ignoring: %m");
return 0;
}
- r = dns_name_normalize(hostname, &normalized);
- if (r < 0) {
- log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname);
- return 0;
- }
-
- if (is_localhost(normalized)) {
- log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring.");
- return 0;
- }
-
- free(lease->hostname);
- lease->hostname = normalized;
- normalized = NULL;
-
break;
- }
- case DHCP_OPTION_ROOT_PATH:
+ case SD_DHCP_OPTION_ROOT_PATH:
r = lease_parse_string(option, len, &lease->root_path);
if (r < 0)
log_debug_errno(r, "Failed to parse root path, ignoring: %m");
break;
- case DHCP_OPTION_RENEWAL_T1_TIME:
+ case SD_DHCP_OPTION_RENEWAL_T1_TIME:
r = lease_parse_u32(option, len, &lease->t1, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
break;
- case DHCP_OPTION_REBINDING_T2_TIME:
+ case SD_DHCP_OPTION_REBINDING_T2_TIME:
r = lease_parse_u32(option, len, &lease->t2, 1);
if (r < 0)
log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
break;
- case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
r = lease_parse_classless_routes(
option, len,
&lease->static_route,
@@ -614,7 +625,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
break;
- case DHCP_OPTION_NEW_TZDB_TIMEZONE: {
+ case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
_cleanup_free_ char *tz = NULL;
r = lease_parse_string(option, len, &tz);
@@ -635,7 +646,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
break;
}
- case DHCP_OPTION_VENDOR_SPECIFIC:
+ case SD_DHCP_OPTION_VENDOR_SPECIFIC:
if (len <= 0)
lease->vendor_specific = mfree(lease->vendor_specific);
@@ -653,7 +664,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->vendor_specific_len = len;
break;
- case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST:
+ case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0)
return r;
@@ -722,7 +733,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
size_t client_id_len, data_len;
const char *string;
uint16_t mtu;
- struct sd_dhcp_route *routes;
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
uint32_t t1, t2, lifetime;
int r;
@@ -839,7 +850,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
LIST_FOREACH(options, option, lease->private_options) {
char key[strlen("OPTION_000")+1];
- snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag);
+ xsprintf(key, "OPTION_%" PRIu8, option->tag);
r = serialize_dhcp_option(f, key, option->data, option->length);
if (r < 0)
goto fail;
@@ -882,7 +893,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
*lifetime = NULL,
*t1 = NULL,
*t2 = NULL,
- *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {};
+ *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
int r, i;
@@ -1050,7 +1061,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
- for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) {
+ for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) {
_cleanup_free_ void *data = NULL;
size_t len;
@@ -1063,7 +1074,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
continue;
}
- r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len);
+ r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len);
if (r < 0)
return r;
}
@@ -1141,3 +1152,27 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
*tz = lease->timezone;
return 0;
}
+
+int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
+ assert_return(route, -EINVAL);
+ assert_return(destination, -EINVAL);
+
+ *destination = route->dst_addr;
+ return 0;
+}
+
+int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
+ assert_return(route, -EINVAL);
+ assert_return(length, -EINVAL);
+
+ *length = route->dst_prefixlen;
+ return 0;
+}
+
+int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
+ assert_return(route, -EINVAL);
+ assert_return(gateway, -EINVAL);
+
+ *gateway = route->gw_addr;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index 87ad595a1a..0b8d4bb843 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -354,13 +354,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
assert(packet);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_SERVER_IDENTIFIER,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER,
4, &server->address);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
- DHCP_OPTION_END, 0, NULL);
+ SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -457,18 +457,18 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
if (r < 0)
return r;
@@ -494,25 +494,25 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
lease_time = htobe32(req->lifetime);
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
&lease_time);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+ SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
if (r < 0)
return r;
r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_ROUTER, 4, &server->address);
+ SD_DHCP_OPTION_ROUTER, 4, &server->address);
if (r < 0)
return r;
if (server->n_dns > 0) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
sizeof(struct in_addr) * server->n_dns, server->dns);
if (r < 0)
return r;
@@ -521,7 +521,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
if (server->n_ntp > 0) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_NTP_SERVER,
+ SD_DHCP_OPTION_NTP_SERVER,
sizeof(struct in_addr) * server->n_ntp, server->ntp);
if (r < 0)
return r;
@@ -530,7 +530,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
if (server->timezone) {
r = dhcp_option_append(
&packet->dhcp, req->max_optlen, &offset, 0,
- DHCP_OPTION_NEW_TZDB_TIMEZONE,
+ SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
strlen(server->timezone), server->timezone);
if (r < 0)
return r;
@@ -576,7 +576,7 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
return r;
r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
- &optoffset, 0, DHCP_OPTION_END, 0, NULL);
+ &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
@@ -596,22 +596,22 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
assert(req);
switch(code) {
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
if (len == 4)
req->lifetime = be32toh(*(be32_t*)option);
break;
- case DHCP_OPTION_REQUESTED_IP_ADDRESS:
+ case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
if (len == 4)
req->requested_ip = *(be32_t*)option;
break;
- case DHCP_OPTION_SERVER_IDENTIFIER:
+ case SD_DHCP_OPTION_SERVER_IDENTIFIER:
if (len == 4)
req->server_id = *(be32_t*)option;
break;
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
if (len >= 2) {
uint8_t *data;
@@ -625,7 +625,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
}
break;
- case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+ case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
if (len == 2)
req->max_optlen = be16toh(*(be16_t*)option) -
- sizeof(DHCPPacket);
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index b8fae1e805..0e7327b895 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -72,10 +72,10 @@ struct sd_dhcp6_client {
};
static const uint16_t default_req_opts[] = {
- DHCP6_OPTION_DNS_SERVERS,
- DHCP6_OPTION_DOMAIN_LIST,
- DHCP6_OPTION_NTP_SERVER,
- DHCP6_OPTION_SNTP_SERVERS,
+ SD_DHCP6_OPTION_DNS_SERVERS,
+ SD_DHCP6_OPTION_DOMAIN_LIST,
+ SD_DHCP6_OPTION_NTP_SERVER,
+ SD_DHCP6_OPTION_SNTP_SERVERS,
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
@@ -245,10 +245,10 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
switch(option) {
- case DHCP6_OPTION_DNS_SERVERS:
- case DHCP6_OPTION_DOMAIN_LIST:
- case DHCP6_OPTION_SNTP_SERVERS:
- case DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_NTP_SERVER:
break;
default:
@@ -362,7 +362,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
message->type = DHCP6_SOLICIT;
r = dhcp6_option_append(&opt, &optlen,
- DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
@@ -380,7 +380,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
message->type = DHCP6_RENEW;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
client->lease->serverid);
if (r < 0)
@@ -406,14 +406,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return -EINVAL;
}
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
client->req_opts_len * sizeof(be16_t),
client->req_opts);
if (r < 0)
return r;
assert (client->duid_len);
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
if (r < 0)
return r;
@@ -424,7 +424,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
else
elapsed_time = 0xffff;
- r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
@@ -687,7 +687,7 @@ static int client_parse_message(sd_dhcp6_client *client,
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
&optval)) >= 0) {
switch (optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
if (clientid) {
log_dhcp6_client(client, "%s contains multiple clientids",
dhcp6_message_type_to_string(message->type));
@@ -705,7 +705,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r >= 0 && id) {
log_dhcp6_client(client, "%s contains multiple serverids",
@@ -719,7 +719,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
return -EINVAL;
@@ -729,7 +729,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_STATUS_CODE:
+ case SD_DHCP6_OPTION_STATUS_CODE:
if (optlen < 2)
return -EINVAL;
@@ -743,7 +743,7 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
@@ -767,35 +767,35 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
- case DHCP6_OPTION_RAPID_COMMIT:
+ case SD_DHCP6_OPTION_RAPID_COMMIT:
r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_set_dns(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_set_domains(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_NTP_SERVER:
+ case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_set_ntp(lease, optval, optlen);
if (r < 0)
return r;
break;
- case DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_set_sntp(lease, optval, optlen);
if (r < 0)
return r;
@@ -1285,7 +1285,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
client->n_ref = 1;
- client->ia_na.type = DHCP6_OPTION_IA_NA;
+ client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
client->index = -1;
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 3f32ba35e7..327759e180 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -256,7 +256,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL);
- free(lease->ntp);
+ lease->ntp = mfree(lease->ntp);
lease->ntp_count = 0;
lease->ntp_allocated = 0;
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index d8154f0587..0ee466b32a 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -1,3 +1,5 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
/***
This file is part of systemd.
@@ -112,7 +114,7 @@ static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
}
static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
- _cleanup_free_ NDiscPrefix *prefix = NULL;
+ NDiscPrefix *prefix;
assert(ret);
@@ -125,8 +127,6 @@ static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
prefix->nd = nd;
*ret = prefix;
- prefix = NULL;
-
return 0;
}
@@ -314,7 +314,6 @@ static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
if (prefix->valid_until < time_now) {
prefix = ndisc_prefix_unref(prefix);
-
continue;
}
@@ -355,14 +354,13 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
prefix_opt->nd_opt_pi_prefix_len, &prefix);
+ if (r < 0) {
+ if (r != -EADDRNOTAVAIL)
+ return r;
- if (r < 0 && r != -EADDRNOTAVAIL)
- return r;
-
- /* if router advertisment prefix valid timeout is zero, the timeout
- callback will be called immediately to clean up the prefix */
+ /* if router advertisment prefix valid timeout is zero, the timeout
+ callback will be called immediately to clean up the prefix */
- if (r == -EADDRNOTAVAIL) {
r = ndisc_prefix_new(nd, &prefix);
if (r < 0)
return r;
@@ -373,9 +371,9 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
sizeof(prefix->addr));
log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime_valid,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
+ SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
+ prefix->len, lifetime_valid,
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
LIST_PREPEND(prefixes, nd->prefixes, prefix);
@@ -386,17 +384,17 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
- prefix->len,
- prefix_opt->nd_opt_pi_prefix_len,
- prefixlen);
+ prefix->len,
+ prefix_opt->nd_opt_pi_prefix_len,
+ prefixlen);
prefix->len = prefixlen;
}
log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime_valid,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
+ SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
+ prefix->len, lifetime_valid,
+ format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
}
r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
@@ -450,7 +448,7 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len
nd->mtu = MAX(mtu, IP6_MIN_MTU);
log_ndisc(nd, "Router Advertisement link MTU %d using %d",
- mtu, nd->mtu);
+ mtu, nd->mtu);
}
break;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 45817d8c36..31832d2d1e 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -77,26 +77,26 @@ static void test_request_basic(sd_event *e) {
assert_se(sd_dhcp_client_set_index(client, 1) == 0);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+ SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_ROUTER) == -EEXIST);
+ SD_DHCP_OPTION_ROUTER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_HOST_NAME) == -EEXIST);
+ SD_DHCP_OPTION_HOST_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PAD) == -EINVAL);
+ SD_DHCP_OPTION_PAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_END) == -EINVAL);
+ SD_DHCP_OPTION_END) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+ SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_OVERLOAD) == -EINVAL);
+ SD_DHCP_OPTION_OVERLOAD) == -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
@@ -122,7 +122,7 @@ static void test_checksum(void) {
static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
switch(code) {
- case DHCP_OPTION_CLIENT_IDENTIFIER:
+ case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
{
uint32_t iaid;
struct duid duid;
@@ -393,7 +393,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
assert_se(res == DHCP_REQUEST);
assert_se(xid == request->xid);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
if (verbose)
printf(" recv DHCP Request 0x%08x\n", be32toh(xid));
@@ -422,7 +422,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
res = dhcp_option_parse(discover, size, check_options, NULL, NULL);
assert_se(res == DHCP_DISCOVER);
- assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
+ assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END);
xid = discover->xid;
diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c
index 75d22c4df3..7b80a5bd90 100644
--- a/src/libsystemd-network/test-dhcp-option.c
+++ b/src/libsystemd-network/test-dhcp-option.c
@@ -29,7 +29,7 @@ static bool verbose = false;
static struct option_desc option_tests[] = {
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
{ {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+ SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
{ {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
{ {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
@@ -37,17 +37,17 @@ static struct option_desc option_tests[] = {
0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
40, true, },
- { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+ { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
42, 3, 0, 0, 0 }, 8, true, },
{ {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
{ {}, 0,
- { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
- { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+ { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+ { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
- { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+ { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
{ 222, 3, 1, 2, 3 }, 5,
- { DHCP_OPTION_OVERLOAD, 1,
+ { SD_DHCP_OPTION_OVERLOAD, 1,
DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
};
@@ -129,12 +129,12 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) {
while (*descpos < *desclen) {
switch(descoption[*descpos]) {
- case DHCP_OPTION_PAD:
+ case SD_DHCP_OPTION_PAD:
*descpos += 1;
break;
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_OVERLOAD:
+ case SD_DHCP_OPTION_MESSAGE_TYPE:
+ case SD_DHCP_OPTION_OVERLOAD:
*descpos += 3;
break;
@@ -157,10 +157,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *
if (!desc)
return -EINVAL;
- assert_se(code != DHCP_OPTION_PAD);
- assert_se(code != DHCP_OPTION_END);
- assert_se(code != DHCP_OPTION_MESSAGE_TYPE);
- assert_se(code != DHCP_OPTION_OVERLOAD);
+ assert_se(code != SD_DHCP_OPTION_PAD);
+ assert_se(code != SD_DHCP_OPTION_END);
+ assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE);
+ assert_se(code != SD_DHCP_OPTION_OVERLOAD);
while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
@@ -298,27 +298,27 @@ static void test_option_set(void) {
result->options[2] = 'C';
result->options[3] = 'D';
- assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 0);
offset = 4;
- assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) == -ENOBUFS);
assert_se(offset == 4);
- assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
+ assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD,
0, NULL) >= 0);
assert_se(offset == 5);
offset = pos = 4;
len = 11;
- while (pos < len && options[pos] != DHCP_OPTION_END) {
+ while (pos < len && options[pos] != SD_DHCP_OPTION_END) {
assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
options[pos],
options[pos + 1],
&options[pos + 2]) >= 0);
- if (options[pos] == DHCP_OPTION_PAD)
+ if (options[pos] == SD_DHCP_OPTION_PAD)
pos++;
else
pos += 2 + options[pos + 1];
@@ -336,15 +336,15 @@ static void test_option_set(void) {
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
- DHCP_OPTION_END);
+ SD_DHCP_OPTION_END);
- assert_se(result->options[9] == DHCP_OPTION_END);
+ assert_se(result->options[9] == SD_DHCP_OPTION_END);
if (verbose)
printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
- DHCP_OPTION_PAD);
+ SD_DHCP_OPTION_PAD);
- assert_se(result->options[10] == DHCP_OPTION_PAD);
+ assert_se(result->options[10] == SD_DHCP_OPTION_PAD);
for (i = 0; i < pos - 8; i++) {
if (verbose)
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index 0ba9adff0a..4ad2e42b86 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -115,10 +115,10 @@ static void test_message_handler(void) {
.message.hlen = ETHER_ADDR_LEN,
.message.xid = htobe32(0x12345678),
.message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' },
- .option_type.code = DHCP_OPTION_MESSAGE_TYPE,
+ .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE,
.option_type.length = 1,
.option_type.type = DHCP_DISCOVER,
- .end = DHCP_OPTION_END,
+ .end = SD_DHCP_OPTION_END,
};
struct in_addr address_lo = {
.s_addr = htonl(INADDR_LOOPBACK),
@@ -134,14 +134,14 @@ static void test_message_handler(void) {
test.end = 0;
/* TODO, shouldn't this fail? */
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
- test.end = DHCP_OPTION_END;
+ test.end = SD_DHCP_OPTION_END;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
test.option_type.code = 0;
test.option_type.length = 0;
test.option_type.type = 0;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_type.code = DHCP_OPTION_MESSAGE_TYPE;
+ test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE;
test.option_type.length = 1;
test.option_type.type = DHCP_DISCOVER;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
@@ -163,11 +163,11 @@ static void test_message_handler(void) {
test.option_type.type = DHCP_REQUEST;
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
- test.option_requested_ip.code = DHCP_OPTION_REQUESTED_IP_ADDRESS;
+ test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS;
test.option_requested_ip.length = 4;
test.option_requested_ip.address = htobe32(0x12345678);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK);
- test.option_server_id.code = DHCP_OPTION_SERVER_IDENTIFIER;
+ test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER;
test.option_server_id.length = 4;
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
@@ -182,7 +182,7 @@ static void test_message_handler(void) {
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
- test.option_client_id.code = DHCP_OPTION_CLIENT_IDENTIFIER;
+ test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER;
test.option_client_id.length = 7;
test.option_client_id.id[0] = 0x01;
test.option_client_id.id[1] = 'A';
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 974d9ef6ac..93f585b8d4 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -70,11 +70,11 @@ static int test_client_basic(sd_event *e) {
sizeof (mac_addr),
ARPHRD_ETHER) >= 0);
- 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) == -EEXIST);
- assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
@@ -88,9 +88,9 @@ static int test_client_basic(sd_event *e) {
static int test_option(sd_event *e) {
uint8_t packet[] = {
'F', 'O', 'O',
- 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
+ 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
'A', 'B', 'C', 'D', 'E', 'F', 'G',
- 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
+ 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'B', 'A', 'R',
};
@@ -124,7 +124,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_ORO);
+ assert_se(optcode == SD_DHCP6_OPTION_ORO);
assert_se(optlen == 7);
assert_se(buflen + pos == sizeof(packet));
@@ -137,7 +137,7 @@ static int test_option(sd_event *e) {
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
- assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
+ assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS);
assert_se(optlen == 9);
assert_se(buflen + pos == sizeof(packet));
@@ -232,13 +232,13 @@ static int test_advertise_option(sd_event *e) {
&optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(optlen == 14);
opt_clientid = true;
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(optlen == 94);
assert_se(!memcmp(optval, &msg_advertise[26], optlen));
@@ -257,7 +257,7 @@ static int test_advertise_option(sd_event *e) {
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(optlen == 14);
assert_se(!memcmp(optval, &msg_advertise[179], optlen));
@@ -265,7 +265,7 @@ static int test_advertise_option(sd_event *e) {
optlen) >= 0);
break;
- case DHCP6_OPTION_PREFERENCE:
+ case SD_DHCP6_OPTION_PREFERENCE:
assert_se(optlen == 1);
assert_se(!*optval);
@@ -273,24 +273,24 @@ static int test_advertise_option(sd_event *e) {
*optval) >= 0);
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(optlen == 2);
break;
- case DHCP6_OPTION_DNS_SERVERS:
+ case SD_DHCP6_OPTION_DNS_SERVERS:
assert_se(optlen == 16);
assert_se(dhcp6_lease_set_dns(lease, optval,
optlen) >= 0);
break;
- case DHCP6_OPTION_DOMAIN_LIST:
+ case SD_DHCP6_OPTION_DOMAIN_LIST:
assert_se(optlen == 11);
assert_se(dhcp6_lease_set_domains(lease, optval,
optlen) >= 0);
break;
- case DHCP6_OPTION_SNTP_SERVERS:
+ case SD_DHCP6_OPTION_SNTP_SERVERS:
assert_se(optlen == 16);
assert_se(dhcp6_lease_set_sntp(lease, optval,
optlen) >= 0);
@@ -379,7 +379,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
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);
+ assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
if (verbose)
printf(" got DHCPv6 event %d\n", event);
@@ -425,7 +425,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -434,7 +434,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -453,7 +453,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_se(!found_serverid);
found_serverid = true;
@@ -462,7 +462,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -521,7 +521,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -530,7 +530,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_se(!found_iana);
found_iana = true;
@@ -540,7 +540,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
@@ -614,7 +614,7 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
while ((r = dhcp6_option_parse(&option, &len,
&optcode, &optlen, &optval)) >= 0) {
switch(optcode) {
- case DHCP6_OPTION_CLIENTID:
+ case SD_DHCP6_OPTION_CLIENTID:
assert_se(!found_clientid);
found_clientid = true;
@@ -623,17 +623,17 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
break;
- case DHCP6_OPTION_IA_NA:
+ case SD_DHCP6_OPTION_IA_NA:
assert_not_reached("IA TA option must not be present");
break;
- case DHCP6_OPTION_SERVERID:
+ case SD_DHCP6_OPTION_SERVERID:
assert_not_reached("Server ID option must not be present");
break;
- case DHCP6_OPTION_ELAPSED_TIME:
+ case SD_DHCP6_OPTION_ELAPSED_TIME:
assert_se(!found_elapsed_time);
found_elapsed_time = true;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 6e2594d001..e344b3b77b 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -75,6 +75,12 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_CONNECTION_FAILURE, ECONNREFUSED),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 7a5f6cda87..130779e8e3 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -77,6 +77,9 @@
#define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed"
#define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor"
#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported"
+#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink"
+#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
+#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c
index 404eaa3c89..c77eb5fd03 100644
--- a/src/libsystemd/sd-bus/bus-error.c
+++ b/src/libsystemd/sd-bus/bus-error.c
@@ -93,14 +93,14 @@ static int bus_error_name_to_errno(const char *name) {
p = startswith(name, "System.Error.");
if (p) {
r = errno_from_name(p);
- if (r <= 0)
+ if (r < 0)
return EIO;
return r;
}
- if (additional_error_maps) {
- for (map = additional_error_maps; *map; map++) {
+ if (additional_error_maps)
+ for (map = additional_error_maps; *map; map++)
for (m = *map;; m++) {
/* For additional error maps the end marker is actually the end marker */
if (m->code == BUS_ERROR_MAP_END_MARKER)
@@ -109,15 +109,13 @@ static int bus_error_name_to_errno(const char *name) {
if (streq(m->name, name))
return m->code;
}
- }
- }
m = __start_BUS_ERROR_MAP;
while (m < __stop_BUS_ERROR_MAP) {
/* For magic ELF error maps, the end marker might
* appear in the middle of things, since multiple maps
* might appear in the same section. Hence, let's skip
- * over it, but realign the pointer to the netx 8byte
+ * over it, but realign the pointer to the next 8 byte
* boundary, which is the selected alignment for the
* arrays. */
if (m->code == BUS_ERROR_MAP_END_MARKER) {
@@ -258,25 +256,24 @@ int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_li
if (!name)
return 0;
- if (!e)
- goto finish;
- assert_return(!bus_error_is_dirty(e), -EINVAL);
+ if (e) {
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
- /* If we hit OOM on formatting the pretty message, we ignore
- * this, since we at least managed to write the error name */
- if (format)
- (void) vasprintf((char**) &e->message, format, ap);
+ /* If we hit OOM on formatting the pretty message, we ignore
+ * this, since we at least managed to write the error name */
+ if (format)
+ (void) vasprintf((char**) &e->message, format, ap);
- e->_need_free = 1;
+ e->_need_free = 1;
+ }
-finish:
return -bus_error_name_to_errno(name);
}
@@ -582,27 +579,29 @@ const char *bus_error_message(const sd_bus_error *e, int error) {
return strerror(error);
}
+static bool map_ok(const sd_bus_error_map *map) {
+ for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
+ if (!map->name || map->code <=0)
+ return false;
+ return true;
+}
+
_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
const sd_bus_error_map **maps = NULL;
unsigned n = 0;
assert_return(map, -EINVAL);
+ assert_return(map_ok(map), -EINVAL);
- if (additional_error_maps) {
- for (;; n++) {
- if (additional_error_maps[n] == NULL)
- break;
-
+ if (additional_error_maps)
+ for (; additional_error_maps[n] != NULL; n++)
if (additional_error_maps[n] == map)
return 0;
- }
- }
maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2);
if (!maps)
return -ENOMEM;
-
maps[n] = map;
maps[n+1] = NULL;
diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c
index 6c05444e9a..e7d6170eec 100644
--- a/src/libsystemd/sd-bus/bus-kernel.c
+++ b/src/libsystemd/sd-bus/bus-kernel.c
@@ -47,6 +47,7 @@
#include "formats-util.h"
#include "memfd-util.h"
#include "parse-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@@ -269,8 +270,8 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
struct bus_body_part *part;
struct kdbus_item *d;
const char *destination;
- bool well_known;
- uint64_t unique;
+ bool well_known = false;
+ uint64_t dst_id;
size_t sz, dl;
unsigned i;
int r;
@@ -287,13 +288,21 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
destination = m->destination ?: m->destination_ptr;
if (destination) {
- r = bus_kernel_parse_unique_name(destination, &unique);
+ r = bus_kernel_parse_unique_name(destination, &dst_id);
if (r < 0)
return r;
-
- well_known = r == 0;
+ if (r == 0) {
+ well_known = true;
+
+ /* verify_destination_id will usually be 0, which makes the kernel
+ * driver only look at the provided well-known name. Otherwise,
+ * the kernel will make sure the provided destination id matches
+ * the owner of the provided well-known-name, and fail if they
+ * differ. Currently, this is only needed for bus-proxyd. */
+ dst_id = m->verify_destination_id;
+ }
} else
- well_known = false;
+ dst_id = KDBUS_DST_ID_BROADCAST;
sz = offsetof(struct kdbus_msg, items);
@@ -331,15 +340,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0) |
((m->header->type == SD_BUS_MESSAGE_SIGNAL) ? KDBUS_MSG_SIGNAL : 0);
- if (well_known)
- /* verify_destination_id will usually be 0, which makes the kernel driver only look
- * at the provided well-known name. Otherwise, the kernel will make sure the provided
- * destination id matches the owner of the provided weel-known-name, and fail if they
- * differ. Currently, this is only needed for bus-proxyd. */
- m->kdbus->dst_id = m->verify_destination_id;
- else
- m->kdbus->dst_id = destination ? unique : KDBUS_DST_ID_BROADCAST;
-
+ m->kdbus->dst_id = dst_id;
m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
m->kdbus->cookie = m->header->dbus2.cookie;
m->kdbus->priority = m->priority;
@@ -849,7 +850,8 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
if (k->src_id == KDBUS_SRC_ID_KERNEL)
bus_message_set_sender_driver(bus, m);
else {
- snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
+ xsprintf(m->sender_buffer, ":1.%llu",
+ (unsigned long long)k->src_id);
m->sender = m->creds.unique_name = m->sender_buffer;
}
@@ -860,7 +862,8 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
else if (k->dst_id == KDBUS_DST_ID_NAME)
m->destination = bus->unique_name; /* fill in unique name if the well-known name is missing */
else {
- snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
+ xsprintf(m->destination_buffer, ":1.%llu",
+ (unsigned long long)k->dst_id);
m->destination = m->destination_buffer;
}
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index c52405463e..407fd14555 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -44,7 +44,15 @@ static void test_error(void) {
assert_se(sd_bus_error_is_set(&error));
sd_bus_error_free(&error);
+ /* Check with no error */
assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_setf(&error, NULL, "yyy %i", -1) == 0);
+ assert_se(error.name == NULL);
+ assert_se(error.message == NULL);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND));
+ assert_se(sd_bus_error_get_errno(&error) == 0);
+ assert_se(!sd_bus_error_is_set(&error));
+
assert_se(sd_bus_error_setf(&error, SD_BUS_ERROR_FILE_NOT_FOUND, "yyy %i", -1) == -ENOENT);
assert_se(streq(error.name, SD_BUS_ERROR_FILE_NOT_FOUND));
assert_se(streq(error.message, "yyy -1"));
@@ -112,6 +120,16 @@ static void test_error(void) {
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
assert_se(sd_bus_error_get_errno(&error) == EIO);
assert_se(sd_bus_error_is_set(&error));
+ sd_bus_error_free(&error);
+
+ /* Check with no error */
+ assert_se(!sd_bus_error_is_set(&error));
+ assert_se(sd_bus_error_set_errnof(&error, 0, "Waldi %c", 'X') == 0);
+ assert_se(error.name == NULL);
+ assert_se(error.message == NULL);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR));
+ assert_se(sd_bus_error_get_errno(&error) == 0);
+ assert_se(!sd_bus_error_is_set(&error));
}
extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
@@ -167,6 +185,16 @@ static const sd_bus_error_map test_errors4[] = {
SD_BUS_ERROR_MAP_END
};
+static const sd_bus_error_map test_errors_bad1[] = {
+ SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", 0),
+ SD_BUS_ERROR_MAP_END
+};
+
+static const sd_bus_error_map test_errors_bad2[] = {
+ SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-1", -1),
+ SD_BUS_ERROR_MAP_END
+};
+
static void test_errno_mapping_custom(void) {
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5);
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52);
@@ -190,6 +218,9 @@ static void test_errno_mapping_custom(void) {
assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO);
assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT);
+
+ assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL);
+ assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL);
}
int main(int argc, char *argv[]) {
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index f44054a7b5..9633e46ce0 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -494,7 +494,7 @@ static int handle_uevent_line(sd_device *device, const char *key, const char *va
int device_read_uevent_file(sd_device *device) {
_cleanup_free_ char *uevent = NULL;
- const char *syspath, *key, *value, *major = NULL, *minor = NULL;
+ const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
char *path;
size_t uevent_len;
unsigned i;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 7fccbd1a71..11c7330b9b 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -225,6 +225,7 @@ struct sd_event {
bool exit_requested:1;
bool need_process_child:1;
bool watchdog:1;
+ bool profile_delays:1;
int exit_code;
@@ -236,6 +237,9 @@ struct sd_event {
unsigned n_sources;
LIST_HEAD(sd_event_source, sources);
+
+ usec_t last_run, last_log;
+ unsigned delays[sizeof(usec_t) * 8];
};
static void source_disconnect(sd_event_source *s);
@@ -444,6 +448,11 @@ _public_ int sd_event_new(sd_event** ret) {
goto fail;
}
+ if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
+ log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
+ e->profile_delays = true;
+ }
+
*ret = e;
return 0;
@@ -652,8 +661,10 @@ static int event_make_signal_data(
d->priority = priority;
r = hashmap_put(e->signal_data, &d->priority, d);
- if (r < 0)
+ if (r < 0) {
+ free(d);
return r;
+ }
added = true;
}
@@ -2622,6 +2633,18 @@ _public_ int sd_event_dispatch(sd_event *e) {
return 1;
}
+static void event_log_delays(sd_event *e) {
+ char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1];
+ unsigned i;
+ int o;
+
+ for (i = o = 0; i < ELEMENTSOF(e->delays); i++) {
+ o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]);
+ e->delays[i] = 0;
+ }
+ log_debug("Event loop iterations: %.*s", o, b);
+}
+
_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
int r;
@@ -2630,11 +2653,30 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
+ if (e->profile_delays && e->last_run) {
+ usec_t this_run;
+ unsigned l;
+
+ this_run = now(CLOCK_MONOTONIC);
+
+ l = u64log2(this_run - e->last_run);
+ assert(l < sizeof(e->delays));
+ e->delays[l]++;
+
+ if (this_run - e->last_log >= 5*USEC_PER_SEC) {
+ event_log_delays(e);
+ e->last_log = this_run;
+ }
+ }
+
r = sd_event_prepare(e);
if (r == 0)
/* There was nothing? Then wait... */
r = sd_event_wait(e, timeout);
+ if (e->profile_delays)
+ e->last_run = now(CLOCK_MONOTONIC);
+
if (r > 0) {
/* There's something now, then let's dispatch it */
r = sd_event_dispatch(e);
@@ -2711,6 +2753,12 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
assert_return(e, -EINVAL);
assert_return(usec, -EINVAL);
assert_return(!event_pid_changed(e), -ECHILD);
+ assert_return(IN_SET(clock,
+ CLOCK_REALTIME,
+ CLOCK_REALTIME_ALARM,
+ CLOCK_MONOTONIC,
+ CLOCK_BOOTTIME,
+ CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP);
if (!dual_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran
@@ -2730,8 +2778,7 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
*usec = e->timestamp.monotonic;
break;
- case CLOCK_BOOTTIME:
- case CLOCK_BOOTTIME_ALARM:
+ default:
*usec = e->timestamp_boottime;
break;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 9417a8d1d1..c605b18ae9 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -264,6 +264,30 @@ static void test_basic(void) {
safe_close_pair(k);
}
+static void test_sd_event_now(void) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ uint64_t event_now;
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0);
+ assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+ assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+
+ assert_se(sd_event_run(e, 0) == 0);
+
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0);
+ assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0);
+ assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP);
+ assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP);
+}
+
static int last_rtqueue_sigval = 0;
static int n_rtqueue = 0;
@@ -324,7 +348,11 @@ static void test_rtqueue(void) {
int main(int argc, char *argv[]) {
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
+
test_basic();
+ test_sd_event_now();
test_rtqueue();
return 0;
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index 4b46eeb533..ef240c3531 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -810,7 +810,7 @@ _public_ int sd_get_uids(uid_t **users) {
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 135354e5f3..be4ab1373d 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -96,15 +96,6 @@ static const NLType rtnl_link_info_data_macvlan_types[] = {
[IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
};
-static const NLType rtnl_link_bridge_management_types[] = {
- [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 },
-/*
- [IFLA_BRIDGE_VLAN_INFO] = { .type = NETLINK_TYPE_BINARY,
- .len = sizeof(struct bridge_vlan_info), },
-*/
-};
-
static const NLType rtnl_link_info_data_bridge_types[] = {
[IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
[IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index c1f5867ee4..4b7fad9c81 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -95,10 +95,14 @@ _public_ int sd_network_get_ntp(char ***ret) {
return network_get_strv("NTP", ret);
}
-_public_ int sd_network_get_domains(char ***ret) {
+_public_ int sd_network_get_search_domains(char ***ret) {
return network_get_strv("DOMAINS", ret);
}
+_public_ int sd_network_get_route_domains(char ***ret) {
+ return network_get_strv("ROUTE_DOMAINS", ret);
+}
+
static int network_link_get_string(int ifindex, const char *field, char **ret) {
_cleanup_free_ char *s = NULL, *p = NULL;
int r;
@@ -222,10 +226,14 @@ _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "NTP", ret);
}
-_public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
+_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "DOMAINS", ret);
}
+_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
+ return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
+}
+
_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret);
}
@@ -234,26 +242,6 @@ _public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret);
}
-_public_ int sd_network_link_get_wildcard_domain(int ifindex) {
- _cleanup_free_ char *p = NULL, *s = NULL;
- int r;
-
- assert_return(ifindex > 0, -EINVAL);
-
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
-
- r = parse_env_file(p, NEWLINE, "WILDCARD_DOMAIN", &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s))
- return -ENODATA;
-
- return parse_boolean(s);
-}
-
static inline int MONITOR_TO_FD(sd_network_monitor *m) {
return (int) (unsigned long) m - 1;
}
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 5ca41331bd..8ab845eb80 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -539,7 +539,7 @@ static int read_next_mapping(const char* filename,
if (!fgets(line, sizeof(line), f)) {
if (ferror(f))
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 816349c559..931b96fe51 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -88,7 +88,7 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR;
+ colors_enabled() * OUTPUT_COLOR;
}
static int list_sessions(int argc, char *argv[], void *userdata) {
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index d51330fb85..2e14aa2d95 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -139,7 +139,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
errno = 0;
p = getpwuid(uid);
if (!p)
- return errno ? -errno : -ENOENT;
+ return errno > 0 ? -errno : -ENOENT;
return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
}
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 4631f5fc90..9eda4638e5 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -124,7 +124,6 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
return r;
seat = session->seat;
-
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat.");
} else {
@@ -1111,7 +1110,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
errno = 0;
pw = getpwuid(uid);
if (!pw)
- return errno ? -errno : -ENOENT;
+ return errno > 0 ? -errno : -ENOENT;
r = bus_verify_polkit_async(
message,
@@ -1995,7 +1994,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
- const char *tty;
+ const char *tty = NULL;
(void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
(void) sd_bus_creds_get_tty(creds, &tty);
@@ -2752,6 +2751,23 @@ int manager_send_changed(Manager *manager, const char *property, ...) {
l);
}
+static int strdup_job(sd_bus_message *reply, char **job) {
+ const char *j;
+ char *copy;
+ int r;
+
+ r = sd_bus_message_read(reply, "o", &j);
+ if (r < 0)
+ return r;
+
+ copy = strdup(j);
+ if (!copy)
+ return -ENOMEM;
+
+ *job = copy;
+ return 1;
+}
+
int manager_start_slice(
Manager *manager,
const char *slice,
@@ -2767,6 +2783,7 @@ int manager_start_slice(
assert(manager);
assert(slice);
+ assert(job);
r = sd_bus_message_new_method_call(
manager->bus,
@@ -2820,22 +2837,7 @@ int manager_start_slice(
if (r < 0)
return r;
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_start_scope(
@@ -2856,6 +2858,7 @@ int manager_start_scope(
assert(manager);
assert(scope);
assert(pid > 1);
+ assert(job);
r = sd_bus_message_new_method_call(
manager->bus,
@@ -2930,22 +2933,7 @@ int manager_start_scope(
if (r < 0)
return r;
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
@@ -2954,6 +2942,7 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error,
assert(manager);
assert(unit);
+ assert(job);
r = sd_bus_call_method(
manager->bus,
@@ -2967,22 +2956,7 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error,
if (r < 0)
return r;
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
@@ -2991,6 +2965,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
assert(manager);
assert(unit);
+ assert(job);
r = sd_bus_call_method(
manager->bus,
@@ -3005,9 +2980,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
- if (job)
- *job = NULL;
-
+ *job = NULL;
sd_bus_error_free(error);
return 0;
}
@@ -3015,22 +2988,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
return r;
}
- if (job) {
- const char *j;
- char *copy;
-
- r = sd_bus_message_read(reply, "o", &j);
- if (r < 0)
- return r;
-
- copy = strdup(j);
- if (!copy)
- return -ENOMEM;
-
- *job = copy;
- }
-
- return 1;
+ return strdup_job(reply, job);
}
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) {
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index 1f4936cebe..9d111f737c 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -34,6 +34,7 @@
#include "logind-seat.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "terminal-util.h"
#include "util.h"
@@ -181,7 +182,7 @@ static int vt_allocate(unsigned int vtnr) {
assert(vtnr >= 1);
- snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
+ xsprintf(p, "/dev/tty%u", vtnr);
fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 4ad9740e5e..98f8ea3c78 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -412,13 +412,12 @@ static int user_start_slice(User *u) {
u->manager->user_tasks_max,
&error,
&job);
- if (r < 0) {
- /* we don't fail due to this, let's try to continue */
- if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
- log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", u->slice, bus_error_message(&error, r), error.name);
- } else {
+ if (r >= 0)
u->slice_job = job;
- }
+ else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
+ /* we don't fail due to this, let's try to continue */
+ log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)",
+ u->slice, bus_error_message(&error, r), error.name);
return 0;
}
@@ -868,7 +867,7 @@ int config_parse_tmpfs_size(
errno = 0;
ul = strtoul(rvalue, &f, 10);
- if (errno != 0 || f != e) {
+ if (errno > 0 || f != e) {
log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/login/logind.c b/src/login/logind.c
index 79ea5ddfcb..9723e2f4e0 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -70,7 +70,7 @@ static Manager *manager_new(void) {
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
- m->user_tasks_max = UINT64_C(4096);
+ m->user_tasks_max = UINT64_C(12288);
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
diff --git a/src/login/logind.conf b/src/login/logind.conf
index 81f6695434..6095e482ac 100644
--- a/src/login/logind.conf
+++ b/src/login/logind.conf
@@ -32,4 +32,4 @@
#IdleActionSec=30min
#RuntimeDirectorySize=10%
#RemoveIPC=yes
-#UserTasksMax=4096
+#UserTasksMax=12288
diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c
index d805bcfdca..9d19307236 100644
--- a/src/machine-id-setup/machine-id-setup-main.c
+++ b/src/machine-id-setup/machine-id-setup-main.c
@@ -112,7 +112,7 @@ int main(int argc, char *argv[]) {
if (arg_commit)
r = machine_id_commit(arg_root);
else
- r = machine_id_setup(arg_root);
+ r = machine_id_setup(arg_root, SD_ID128_NULL);
finish:
free(arg_root);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 685bbafdf1..fd454310ae 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -108,7 +108,7 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF;
}
@@ -1571,7 +1571,7 @@ static int start_machine(int argc, char *argv[], void *userdata) {
return log_oom();
}
- r = bus_wait_for_jobs(w, arg_quiet);
+ r = bus_wait_for_jobs(w, arg_quiet, NULL);
if (r < 0)
return r;
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index c6b5b1ec44..28134f61bf 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -910,7 +910,7 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -968,7 +968,7 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1028,7 +1028,7 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1086,7 +1086,7 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
if (k < 0 && feof(f))
break;
if (k != 3) {
- if (ferror(f) && errno != 0)
+ if (ferror(f) && errno > 0)
return -errno;
return -EIO;
@@ -1325,7 +1325,7 @@ int manager_start_scope(
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192);
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384));
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 0234825adb..253692aa64 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -40,6 +40,7 @@
#include "pager.h"
#include "parse-util.h"
#include "socket-util.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -275,7 +276,8 @@ static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) {
if (memcmp(mac, "\0\0\0", 3) == 0)
return -EINVAL;
- snprintf(modalias, sizeof(modalias), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
+ xsprintf(modalias, "OUI:" ETHER_ADDR_FORMAT_STR,
+ ETHER_ADDR_FORMAT_VAL(*mac));
r = sd_hwdb_get(hwdb, modalias, "ID_OUI_FROM_DATABASE", &description);
if (r < 0)
@@ -488,6 +490,9 @@ static int dump_addresses(
static void dump_list(const char *prefix, char **l) {
char **i;
+ if (strv_isempty(l))
+ return;
+
STRV_FOREACH(i, l) {
printf("%*s%s\n",
(int) strlen(prefix),
@@ -500,7 +505,7 @@ static int link_status_one(
sd_netlink *rtnl,
sd_hwdb *hwdb,
const char *name) {
- _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
+ _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
@@ -550,7 +555,6 @@ static int link_status_one(
return rtnl_log_parse_error(r);
have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
-
if (have_mac) {
const uint8_t *p;
bool all_zeroes = true;
@@ -565,44 +569,35 @@ static int link_status_one(
have_mac = false;
}
- sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
+ (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
- sd_network_link_get_operational_state(ifindex, &operational_state);
+ (void) sd_network_link_get_operational_state(ifindex, &operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
- sd_network_link_get_setup_state(ifindex, &setup_state);
+ (void) sd_network_link_get_setup_state(ifindex, &setup_state);
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
- sd_network_link_get_dns(ifindex, &dns);
- sd_network_link_get_domains(ifindex, &domains);
- r = sd_network_link_get_wildcard_domain(ifindex);
- if (r > 0) {
- char *wildcard;
-
- wildcard = strdup("*");
- if (!wildcard)
- return log_oom();
-
- if (strv_consume(&domains, wildcard) < 0)
- return log_oom();
- }
+ (void) sd_network_link_get_dns(ifindex, &dns);
+ (void) sd_network_link_get_search_domains(ifindex, &search_domains);
+ (void) sd_network_link_get_route_domains(ifindex, &route_domains);
+ (void) sd_network_link_get_ntp(ifindex, &ntp);
sprintf(devid, "n%i", ifindex);
- (void)sd_device_new_from_device_id(&d, devid);
+ (void) sd_device_new_from_device_id(&d, devid);
if (d) {
- (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
- (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
- (void)sd_device_get_property_value(d, "ID_PATH", &path);
+ (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
+ (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
+ (void) sd_device_get_property_value(d, "ID_PATH", &path);
r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
if (r < 0)
- (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
+ (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
if (r < 0)
- (void)sd_device_get_property_value(d, "ID_MODEL", &model);
+ (void) sd_device_get_property_value(d, "ID_MODEL", &model);
}
link_get_type_string(iftype, d, &t);
@@ -651,20 +646,14 @@ static int link_status_one(
dump_addresses(rtnl, " Address: ", ifindex);
dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
- if (!strv_isempty(dns))
- dump_list(" DNS: ", dns);
- if (!strv_isempty(domains))
- dump_list(" Domain: ", domains);
+ dump_list(" DNS: ", dns);
+ dump_list(" Search Domains: ", search_domains);
+ dump_list(" Route Domains: ", route_domains);
- (void) sd_network_link_get_ntp(ifindex, &ntp);
- if (!strv_isempty(ntp))
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
- if (!strv_isempty(carrier_bound_to))
- dump_list("Carrier Bound To: ", carrier_bound_to);
-
- if (!strv_isempty(carrier_bound_by))
- dump_list("Carrier Bound By: ", carrier_bound_by);
+ dump_list("Carrier Bound To: ", carrier_bound_to);
+ dump_list("Carrier Bound By: ", carrier_bound_by);
(void) sd_network_link_get_timezone(ifindex, &tz);
if (tz)
@@ -689,30 +678,30 @@ static int link_status(int argc, char *argv[], void *userdata) {
if (argc <= 1 && !arg_all) {
_cleanup_free_ char *operational_state = NULL;
- _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
+ _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains;
const char *on_color_operational, *off_color_operational;
sd_network_get_operational_state(&operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
- printf("%s%s%s State: %s%s%s\n",
+ printf("%s%s%s State: %s%s%s\n",
on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
on_color_operational, strna(operational_state), off_color_operational);
- dump_addresses(rtnl, " Address: ", 0);
- dump_gateways(rtnl, hwdb, " Gateway: ", 0);
+ dump_addresses(rtnl, " Address: ", 0);
+ dump_gateways(rtnl, hwdb, " Gateway: ", 0);
sd_network_get_dns(&dns);
- if (!strv_isempty(dns))
- dump_list(" DNS: ", dns);
+ dump_list(" DNS: ", dns);
+
+ sd_network_get_search_domains(&search_domains);
+ dump_list("Search Domains: ", search_domains);
- sd_network_get_domains(&domains);
- if (!strv_isempty(domains))
- dump_list(" Domain: ", domains);
+ sd_network_get_route_domains(&route_domains);
+ dump_list(" Route Domains: ", route_domains);
sd_network_get_ntp(&ntp);
- if (!strv_isempty(ntp))
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
return 0;
}
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 48e3d84055..59eccb392f 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -54,7 +54,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
static int link_set_dhcp_routes(Link *link) {
struct in_addr gateway;
- struct sd_dhcp_route *static_routes;
+ _cleanup_free_ sd_dhcp_route **static_routes = NULL;
int r, n, i;
assert(link);
@@ -130,9 +130,9 @@ static int link_set_dhcp_routes(Link *link) {
route->family = AF_INET;
route->protocol = RTPROT_DHCP;
- route->gw.in = static_routes[i].gw_addr;
- route->dst.in = static_routes[i].dst_addr;
- route->dst_prefixlen = static_routes[i].dst_prefixlen;
+ assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
route->priority = link->network->dhcp_route_metric;
r = route_configure(route, link, &dhcp4_route_handler);
@@ -158,8 +158,8 @@ static int dhcp_lease_lost(Link *link) {
log_link_warning(link, "DHCP lease lost");
- if (link->network->dhcp_routes) {
- struct sd_dhcp_route *routes;
+ if (link->network->dhcp_use_routes) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
int n, i;
n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
@@ -170,9 +170,9 @@ static int dhcp_lease_lost(Link *link) {
r = route_new(&route);
if (r >= 0) {
route->family = AF_INET;
- route->gw.in = routes[i].gw_addr;
- route->dst.in = routes[i].dst_addr;
- route->dst_prefixlen = routes[i].dst_prefixlen;
+ assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
route_remove(route, link,
&link_route_remove_handler);
@@ -223,7 +223,7 @@ static int dhcp_lease_lost(Link *link) {
}
}
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
@@ -238,11 +238,11 @@ static int dhcp_lease_lost(Link *link) {
}
}
- if (link->network->dhcp_hostname) {
+ if (link->network->dhcp_use_hostname) {
const char *hostname = NULL;
- if (link->network->hostname)
- hostname = link->network->hostname;
+ if (link->network->dhcp_hostname)
+ hostname = link->network->dhcp_hostname;
else
(void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
@@ -412,7 +412,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
link->dhcp_lease = sd_dhcp_lease_ref(lease);
link_dirty(link);
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(lease, &mtu);
@@ -423,11 +423,11 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
- if (link->network->dhcp_hostname) {
+ if (link->network->dhcp_use_hostname) {
const char *hostname = NULL;
- if (link->network->hostname)
- hostname = link->network->hostname;
+ if (link->network->dhcp_hostname)
+ hostname = link->network->dhcp_hostname;
else
(void) sd_dhcp_lease_get_hostname(lease, &hostname);
@@ -438,7 +438,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
- if (link->network->dhcp_timezone) {
+ if (link->network->dhcp_use_timezone) {
const char *tz = NULL;
(void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
@@ -571,45 +571,45 @@ int dhcp4_configure(Link *link) {
return r;
}
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_INTERFACE_MTU);
+ SD_DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
return r;
}
- if (link->network->dhcp_routes) {
+ if (link->network->dhcp_use_routes) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_STATIC_ROUTE);
+ SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
return r;
r = sd_dhcp_client_set_request_option(link->dhcp_client,
- DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
if (r < 0)
return r;
}
- /* Always acquire the timezone and NTP*/
- r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER);
+ /* Always acquire the timezone and NTP */
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
if (r < 0)
return r;
- r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE);
+ r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
if (r < 0)
return r;
- if (link->network->dhcp_sendhost) {
+ if (link->network->dhcp_send_hostname) {
_cleanup_free_ char *hostname = NULL;
const char *hn = NULL;
- if (!link->network->hostname) {
+ if (!link->network->dhcp_hostname) {
hostname = gethostname_malloc();
if (!hostname)
return -ENOMEM;
hn = hostname;
} else
- hn = link->network->hostname;
+ hn = link->network->dhcp_hostname;
if (!is_localhost(hn)) {
r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index d09a3c2d07..4d6ac747fd 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -59,15 +59,19 @@ static char *link_bus_path(Link *link) {
int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
Manager *m = userdata;
+ unsigned c = 0;
Link *link;
Iterator i;
- int r;
assert(bus);
assert(path);
assert(m);
assert(nodes);
+ l = new0(char*, hashmap_size(m->links) + 1);
+ if (!l)
+ return -ENOMEM;
+
HASHMAP_FOREACH(link, m->links, i) {
char *p;
@@ -75,11 +79,10 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
if (!p)
return -ENOMEM;
- r = strv_consume(&l, p);
- if (r < 0)
- return r;
+ l[c++] = p;
}
+ l[c] = NULL;
*nodes = l;
l = NULL;
@@ -99,7 +102,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
assert(found);
r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier);
- if (r < 0)
+ if (r <= 0)
return 0;
r = parse_ifindex(identifier, &ifindex);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 10fec5e75f..a2f0eceb6d 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -767,7 +767,7 @@ static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
addresses[n_addresses++] = ia;
}
- if (link->network->dhcp_dns &&
+ if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
const struct in_addr *da = NULL;
int n;
@@ -812,7 +812,7 @@ static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
addresses[n_addresses++] = ia;
}
- if (link->network->dhcp_ntp &&
+ if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
const struct in_addr *da = NULL;
int n;
@@ -2039,9 +2039,9 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_PENDING);
- /* Drop foreign config, but ignore loopback device.
- * We do not want to remove loopback address. */
- if (!(link->flags & IFF_LOOPBACK)) {
+ /* Drop foreign config, but ignore loopback or critical devices.
+ * We do not want to remove loopback address or addresses used for root NFS. */
+ if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) {
r = link_drop_foreign_config(link);
if (r < 0)
return r;
@@ -2729,9 +2729,10 @@ int link_save(Link *link) {
admin_state, oper_state);
if (link->network) {
- char **address, **domain;
bool space;
sd_dhcp6_lease *dhcp6_lease = NULL;
+ const char *dhcp_domainname = NULL;
+ char **dhcp6_domains = NULL;
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
@@ -2743,14 +2744,9 @@ int link_save(Link *link) {
fputs("DNS=", f);
space = false;
- STRV_FOREACH(address, link->network->dns) {
- if (space)
- fputc(' ', f);
- fputs(*address, f);
- space = true;
- }
+ fputstrv(f, link->network->dns, NULL, &space);
- if (link->network->dhcp_dns &&
+ if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
const struct in_addr *addresses;
@@ -2763,7 +2759,7 @@ int link_save(Link *link) {
}
}
- if (link->network->dhcp_dns && dhcp6_lease) {
+ if (link->network->dhcp_use_dns && dhcp6_lease) {
struct in6_addr *in6_addrs;
r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
@@ -2778,14 +2774,9 @@ int link_save(Link *link) {
fputs("NTP=", f);
space = false;
- STRV_FOREACH(address, link->network->ntp) {
- if (space)
- fputc(' ', f);
- fputs(*address, f);
- space = true;
- }
+ fputstrv(f, link->network->ntp, NULL, &space);
- if (link->network->dhcp_ntp &&
+ if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
const struct in_addr *addresses;
@@ -2798,10 +2789,9 @@ int link_save(Link *link) {
}
}
- if (link->network->dhcp_ntp && dhcp6_lease) {
+ if (link->network->dhcp_use_ntp && dhcp6_lease) {
struct in6_addr *in6_addrs;
char **hosts;
- char **hostname;
r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease,
&in6_addrs);
@@ -2813,58 +2803,41 @@ int link_save(Link *link) {
}
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;
- }
- }
+ if (r > 0)
+ fputstrv(f, hosts, NULL, &space);
}
fputc('\n', f);
- fputs("DOMAINS=", f);
- space = false;
- STRV_FOREACH(domain, link->network->domains) {
- if (space)
- fputc(' ', f);
- fputs(*domain, f);
- space = true;
+ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+ if (link->dhcp_lease)
+ (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
+
+ if (dhcp6_lease)
+ (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
}
- if (link->network->dhcp_domains &&
- link->dhcp_lease) {
- const char *domainname;
+ fputs("DOMAINS=", f);
+ fputstrv(f, link->network->search_domains, NULL, &space);
- r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
- if (r >= 0) {
- if (space)
- fputc(' ', f);
- fputs(domainname, f);
- space = true;
- }
- }
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
- 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;
- }
- }
- }
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
fputc('\n', f);
- fprintf(f, "WILDCARD_DOMAIN=%s\n",
- yes_no(link->network->wildcard_domain));
+ fputs("ROUTE_DOMAINS=", f);
+ fputstrv(f, link->network->route_domains, NULL, NULL);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
+
+ fputc('\n', f);
fprintf(f, "LLMNR=%s\n",
resolve_support_to_string(link->network->llmnr));
@@ -2880,12 +2853,8 @@ int link_save(Link *link) {
fputs("DNSSEC_NTA=", f);
space = false;
- SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i) {
- if (space)
- fputc(' ', f);
- fputs(n, f);
- space = true;
- }
+ SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i)
+ fputs_with_space(f, n, NULL, &space);
fputc('\n', f);
}
@@ -2925,12 +2894,8 @@ int link_save(Link *link) {
bool space = false;
fputs("CARRIER_BOUND_TO=", f);
- HASHMAP_FOREACH(carrier, link->bound_to_links, i) {
- if (space)
- fputc(' ', f);
- fputs(carrier->ifname, f);
- space = true;
- }
+ HASHMAP_FOREACH(carrier, link->bound_to_links, i)
+ fputs_with_space(f, carrier->ifname, NULL, &space);
fputc('\n', f);
}
@@ -2940,12 +2905,8 @@ int link_save(Link *link) {
bool space = false;
fputs("CARRIER_BOUND_BY=", f);
- HASHMAP_FOREACH(carrier, link->bound_by_links, i) {
- if (space)
- fputc(' ', f);
- fputs(carrier->ifname, f);
- space = true;
- }
+ HASHMAP_FOREACH(carrier, link->bound_by_links, i)
+ fputs_with_space(f, carrier->ifname, NULL, &space);
fputc('\n', f);
}
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index aeb6e34c52..c10635d202 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -29,12 +29,14 @@
#include "bus-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "dns-domain.h"
#include "fd-util.h"
#include "fileio.h"
#include "libudev-private.h"
#include "local-addresses.h"
#include "netlink-util.h"
#include "networkd.h"
+#include "ordered-set.h"
#include "path-util.h"
#include "set.h"
#include "udev-util.h"
@@ -659,15 +661,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
} else if (ifindex <= 0) {
log_warning("rtnl: received link message with invalid ifindex: %d", ifindex);
return 0;
- } else
- link_get(m, ifindex, &link);
+ }
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name);
if (r < 0) {
log_warning_errno(r, "rtnl: Received link message without ifname: %m");
return 0;
- } else
- netdev_get(m, name, &netdev);
+ }
+
+ (void) link_get(m, ifindex, &link);
+ (void) netdev_get(m, name, &netdev);
switch (type) {
case RTM_NEWLINK:
@@ -775,7 +778,7 @@ static int manager_connect_rtnl(Manager *m) {
return 0;
}
-static int set_put_in_addr(Set *s, const struct in_addr *address) {
+static int ordered_set_put_in_addr(OrderedSet *s, const struct in_addr *address) {
char *p;
int r;
@@ -785,21 +788,21 @@ static int set_put_in_addr(Set *s, const struct in_addr *address) {
if (r < 0)
return r;
- r = set_consume(s, p);
+ r = ordered_set_consume(s, p);
if (r == -EEXIST)
return 0;
return r;
}
-static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
+static int ordered_set_put_in_addrv(OrderedSet *s, const struct in_addr *addresses, int n) {
int r, i, c = 0;
assert(s);
assert(n <= 0 || addresses);
for (i = 0; i < n; i++) {
- r = set_put_in_addr(s, addresses+i);
+ r = ordered_set_put_in_addr(s, addresses+i);
if (r < 0)
return r;
@@ -809,27 +812,24 @@ static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
return c;
}
-static void print_string_set(FILE *f, const char *field, Set *s) {
+static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
Iterator i;
char *p;
- if (set_isempty(s))
+ if (ordered_set_isempty(s))
return;
fputs(field, f);
- SET_FOREACH(p, s, i) {
- if (space)
- fputc(' ', f);
- fputs(p, f);
- space = true;
- }
+ ORDERED_SET_FOREACH(p, s, i)
+ fputs_with_space(f, p, NULL, &space);
+
fputc('\n', f);
}
static int manager_save(Manager *m) {
- _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
+ _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
Link *link;
Iterator i;
_cleanup_free_ char *temp_path = NULL;
@@ -842,16 +842,20 @@ static int manager_save(Manager *m) {
assert(m->state_file);
/* We add all NTP and DNS server to a set, to filter out duplicates */
- dns = set_new(&string_hash_ops);
+ dns = ordered_set_new(&string_hash_ops);
if (!dns)
return -ENOMEM;
- ntp = set_new(&string_hash_ops);
+ ntp = ordered_set_new(&string_hash_ops);
if (!ntp)
return -ENOMEM;
- domains = set_new(&string_hash_ops);
- if (!domains)
+ search_domains = ordered_set_new(&dns_name_hash_ops);
+ if (!search_domains)
+ return -ENOMEM;
+
+ route_domains = ordered_set_new(&dns_name_hash_ops);
+ if (!route_domains)
return -ENOMEM;
HASHMAP_FOREACH(link, m->links, i) {
@@ -865,15 +869,19 @@ static int manager_save(Manager *m) {
continue;
/* First add the static configured entries */
- r = set_put_strdupv(dns, link->network->dns);
+ r = ordered_set_put_strdupv(dns, link->network->dns);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_put_strdupv(ntp, link->network->ntp);
if (r < 0)
return r;
- r = set_put_strdupv(ntp, link->network->ntp);
+ r = ordered_set_put_strdupv(search_domains, link->network->search_domains);
if (r < 0)
return r;
- r = set_put_strdupv(domains, link->network->domains);
+ r = ordered_set_put_strdupv(route_domains, link->network->route_domains);
if (r < 0)
return r;
@@ -881,36 +889,41 @@ static int manager_save(Manager *m) {
continue;
/* Secondly, add the entries acquired via DHCP */
- if (link->network->dhcp_dns) {
+ if (link->network->dhcp_use_dns) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
if (r > 0) {
- r = set_put_in_addrv(dns, addresses, r);
+ r = ordered_set_put_in_addrv(dns, addresses, r);
if (r < 0)
return r;
} else if (r < 0 && r != -ENODATA)
return r;
}
- if (link->network->dhcp_ntp) {
+ if (link->network->dhcp_use_ntp) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
if (r > 0) {
- r = set_put_in_addrv(ntp, addresses, r);
+ r = ordered_set_put_in_addrv(ntp, addresses, r);
if (r < 0)
return r;
} else if (r < 0 && r != -ENODATA)
return r;
}
- if (link->network->dhcp_domains) {
+ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
const char *domainname;
r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
if (r >= 0) {
- r = set_put_strdup(domains, domainname);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES)
+ r = ordered_set_put_strdup(search_domains, domainname);
+ else
+ r = ordered_set_put_strdup(route_domains, domainname);
+
if (r < 0)
return r;
} else if (r != -ENODATA)
@@ -933,7 +946,8 @@ static int manager_save(Manager *m) {
print_string_set(f, "DNS=", dns);
print_string_set(f, "NTP=", ntp);
- print_string_set(f, "DOMAINS=", domains);
+ print_string_set(f, "DOMAINS=", search_domains);
+ print_string_set(f, "ROUTE_DOMAINS=", route_domains);
r = fflush_and_check(f);
if (r < 0)
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index ce9e513ceb..483efd17c3 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -89,7 +89,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5];
}
address->prefixlen = prefixlen;
- address->flags = IFA_F_NOPREFIXROUTE;
+ address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
address->cinfo.ifa_prefered = lifetime_preferred;
address->cinfo.ifa_valid = lifetime_valid;
diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c
index 385338849f..a2c00161d3 100644
--- a/src/network/networkd-netdev-tunnel.c
+++ b/src/network/networkd-netdev-tunnel.c
@@ -56,7 +56,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin
assert(link);
assert(m);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
@@ -89,7 +89,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink
assert(link);
assert(m);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
if (r < 0)
@@ -126,7 +126,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink
t = GRETAP(netdev);
assert(t);
- assert(t->family == AF_INET);
+ assert(t->family == AF_INET || t->family != -1);
assert(link);
assert(m);
@@ -358,12 +358,7 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
assert(t);
- if (t->remote.in.s_addr == INADDR_ANY) {
- log_warning("Tunnel without remote address configured in %s. Ignoring", filename);
- return -EINVAL;
- }
-
- if (t->family != AF_INET && t->family != AF_INET6) {
+ if (t->family != AF_INET && t->family != AF_INET6 && t->family != 0) {
log_warning("Tunnel with invalid address family configured in %s. Ignoring", filename);
return -EINVAL;
}
@@ -397,15 +392,21 @@ int config_parse_tunnel_address(const char *unit,
assert(rvalue);
assert(data);
- r = in_addr_from_string_auto(rvalue, &f, &buffer);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
+ if (streq(rvalue, "any")) {
+ t->family = 0;
return 0;
- }
+ } else {
- if (t->family != AF_UNSPEC && t->family != f) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
- return 0;
+ r = in_addr_from_string_auto(rvalue, &f, &buffer);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel address is invalid, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ if (t->family != AF_UNSPEC && t->family != f) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
+ return 0;
+ }
}
t->family = f;
@@ -498,6 +499,7 @@ static void ipip_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void sit_init(NetDev *n) {
@@ -507,6 +509,7 @@ static void sit_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void vti_init(NetDev *n) {
@@ -537,6 +540,7 @@ static void gre_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
+ t->family = -1;
}
static void ip6gre_init(NetDev *n) {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 2f2a36ccca..409df1709f 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -43,7 +43,7 @@ Network.IPv6Token, config_parse_ipv6token,
Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
Network.Address, config_parse_address, 0, 0
Network.Gateway, config_parse_gateway, 0, 0
-Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
+Network.Domains, config_parse_domains, 0, 0
Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
@@ -68,19 +68,19 @@ Route.Metric, config_parse_route_priority,
Route.Scope, config_parse_route_scope, 0, 0
Route.PreferredSource, config_parse_preferred_src, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
-DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, hostname)
+DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone)
+DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
@@ -101,9 +101,9 @@ BridgeFDB.MACAddress, config_parse_fdb_hwaddr,
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index c11cb3dcb3..e1a811129d 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -105,11 +105,11 @@ static int network_load_one(Manager *manager, const char *filename) {
*d = '\0';
network->dhcp = ADDRESS_FAMILY_NO;
- network->dhcp_ntp = true;
- network->dhcp_dns = true;
- network->dhcp_hostname = true;
- network->dhcp_routes = true;
- network->dhcp_sendhost = true;
+ network->dhcp_use_ntp = true;
+ network->dhcp_use_dns = true;
+ network->dhcp_use_hostname = true;
+ network->dhcp_use_routes = true;
+ network->dhcp_send_hostname = true;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
@@ -227,13 +227,14 @@ void network_free(Network *network) {
free(network->description);
free(network->dhcp_vendor_class_identifier);
- free(network->hostname);
+ free(network->dhcp_hostname);
free(network->mac);
strv_free(network->ntp);
strv_free(network->dns);
- strv_free(network->domains);
+ strv_free(network->search_domains);
+ strv_free(network->route_domains);
strv_free(network->bind_carrier);
netdev_unref(network->bridge);
@@ -384,7 +385,10 @@ int network_apply(Manager *manager, Network *network, Link *link) {
route->protocol = RTPROT_STATIC;
}
- if (network->dns || network->ntp || network->domains) {
+ if (!strv_isempty(network->dns) ||
+ !strv_isempty(network->ntp) ||
+ !strv_isempty(network->search_domains) ||
+ !strv_isempty(network->route_domains)) {
manager_dirty(manager);
link_dirty(link);
}
@@ -469,49 +473,85 @@ int config_parse_netdev(const char *unit,
return 0;
}
-int config_parse_domains(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) {
- Network *network = userdata;
- char ***domains = data;
- char **domain;
+int config_parse_domains(
+ 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) {
+
+ const char *p;
+ Network *n = data;
int r;
- r = config_parse_strv(unit, filename, line, section, section_line,
- lvalue, ltype, rvalue, domains, userdata);
- if (r < 0)
- return r;
+ assert(n);
+ assert(lvalue);
+ assert(rvalue);
- strv_uniq(*domains);
- network->wildcard_domain = !!strv_find(*domains, "*");
+ if (isempty(rvalue)) {
+ n->search_domains = strv_free(n->search_domains);
+ n->route_domains = strv_free(n->route_domains);
+ return 0;
+ }
- STRV_FOREACH(domain, *domains) {
- if (is_localhost(*domain))
- log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
- else {
- r = dns_name_is_valid(*domain);
- if (r <= 0 && !streq(*domain, "*")) {
- if (r < 0)
- log_error_errno(r, "Failed to validate domain name: %s: %m", *domain);
- if (r == 0)
- log_warning("Domain name is not valid, ignoring assignment: %s", *domain);
- } else
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *w = NULL, *normalized = NULL;
+ const char *domain;
+ bool is_route;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ is_route = w[0] == '~';
+ domain = is_route ? w + 1 : w;
+
+ if (dns_name_is_root(domain) || streq(domain, "*")) {
+ /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
+ * routing domain, unconditionally. */
+ is_route = true;
+ domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
+
+ } else {
+ r = dns_name_normalize(domain, &normalized);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
+ continue;
+ }
+
+ domain = normalized;
+
+ if (is_localhost(domain)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain);
continue;
+ }
}
- strv_remove(*domains, *domain);
+ if (is_route) {
+ r = strv_extend(&n->route_domains, domain);
+ if (r < 0)
+ return log_oom();
- /* We removed one entry, make sure we don't skip the next one */
- domain--;
+ } else {
+ r = strv_extend(&n->search_domains, domain);
+ if (r < 0)
+ return log_oom();
+ }
}
+ strv_uniq(n->route_domains);
+ strv_uniq(n->search_domains);
+
return 0;
}
@@ -930,7 +970,7 @@ int config_parse_dnssec_negative_trust_anchors(
Network *n = data;
int r;
- assert(filename);
+ assert(n);
assert(lvalue);
assert(rvalue);
@@ -965,3 +1005,13 @@ int config_parse_dnssec_negative_trust_anchors(
return 0;
}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
+
+static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
+ [DHCP_USE_DOMAINS_NO] = "no",
+ [DHCP_USE_DOMAINS_ROUTE] = "route",
+ [DHCP_USE_DOMAINS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b07fa41abc..626dfbd40a 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -52,6 +52,14 @@ typedef enum IPv6PrivacyExtensions {
_IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
} IPv6PrivacyExtensions;
+typedef enum DHCPUseDomains {
+ DHCP_USE_DOMAINS_NO,
+ DHCP_USE_DOMAINS_YES,
+ DHCP_USE_DOMAINS_ROUTE,
+ _DHCP_USE_DOMAINS_MAX,
+ _DHCP_USE_DOMAINS_INVALID = -1,
+} DHCPUseDomains;
+
struct Network {
Manager *manager;
@@ -79,17 +87,17 @@ struct Network {
AddressFamilyBoolean dhcp;
DCHPClientIdentifier dhcp_client_identifier;
char *dhcp_vendor_class_identifier;
- char *hostname;
- bool dhcp_dns;
- bool dhcp_ntp;
- bool dhcp_mtu;
- bool dhcp_hostname;
- bool dhcp_domains;
- bool dhcp_sendhost;
+ char *dhcp_hostname;
+ bool dhcp_use_dns;
+ bool dhcp_use_ntp;
+ bool dhcp_use_mtu;
+ bool dhcp_use_hostname;
+ DHCPUseDomains dhcp_use_domains;
+ bool dhcp_send_hostname;
bool dhcp_broadcast;
bool dhcp_critical;
- bool dhcp_routes;
- bool dhcp_timezone;
+ bool dhcp_use_routes;
+ bool dhcp_use_timezone;
unsigned dhcp_route_metric;
/* DHCP Server Support */
@@ -141,8 +149,7 @@ struct Network {
Hashmap *routes_by_section;
Hashmap *fdb_entries_by_section;
- bool wildcard_domain;
- char **domains, **dns, **ntp, **bind_carrier;
+ char **search_domains, **route_domains, **dns, **ntp, **bind_carrier;
ResolveSupport llmnr;
ResolveSupport mdns;
@@ -175,6 +182,7 @@ int config_parse_timezone(const char *unit, const char *filename, unsigned line,
int config_parse_dhcp_server_dns(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_dhcp_server_ntp(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_dnssec_negative_trust_anchors(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_dhcp_use_domains(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);
/* Legacy IPv4LL support */
int config_parse_ipv4ll(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);
@@ -188,3 +196,6 @@ int network_object_find(sd_bus *bus, const char *path, const char *interface, vo
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
+
+const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
+DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index a4e13bd6aa..b183e3aecc 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1027,7 +1027,7 @@ static int setup_timezone(const char *dest) {
}
check = strjoina("/usr/share/zoneinfo/", z);
- check = prefix_root(dest, check);
+ check = prefix_roota(dest, check);
if (laccess(check, F_OK) < 0) {
log_warning("Timezone %s does not exist in container, not updating container timezone.", z);
return 0;
@@ -1482,7 +1482,7 @@ static int setup_journal(const char *directory) {
}
static int drop_capabilities(void) {
- return capability_bounding_set_drop(~arg_retain, false);
+ return capability_bounding_set_drop(arg_retain, false);
}
static int reset_audit_loginuid(void) {
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 40c8ad3a19..dcdbc31a78 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -27,7 +27,6 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "macro.h"
diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c
index bd8e27dc74..2b716a69d1 100644
--- a/src/nss-resolve/nss-resolve.c
+++ b/src/nss-resolve/nss-resolve.c
@@ -29,7 +29,6 @@
#include "sd-bus.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "nss-util.h"
@@ -660,7 +659,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
p = buffer + idx;
memcpy(p, n, l+1);
- if (i > 1)
+ if (i > 0)
((char**) r_aliases)[i-1] = p;
i++;
diff --git a/src/resolve-host/Makefile b/src/resolve-host/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/resolve-host/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
index 33f4dd9cb6..22004a00cd 100644
--- a/src/resolve/RFCs
+++ b/src/resolve/RFCs
@@ -13,14 +13,14 @@ Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- App
Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification
- https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
+Y https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV)
D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6
Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types
- https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
- https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
- https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
+Y https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
+Y https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
+Y https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways
Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification
@@ -31,26 +31,26 @@ Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Si
~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior
Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR)
Y https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors
- https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
+Y https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers
Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework
Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol
Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements
Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones
- https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification
+Y https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification
Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS
! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names
https://tools.ietf.org/html/rfc6762 → Multicast DNS
https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery
- https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
- https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
+~ https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
+Y https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0))
Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status
Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC)
- https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
+Y https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
Y https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors
~ https://tools.ietf.org/html/rfc7719 → DNS Terminology
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index fb8228048d..56720646ca 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
+
#include "dns-type.h"
#include "string-util.h"
@@ -120,6 +122,32 @@ bool dns_type_may_redirect(uint16_t type) {
DNS_TYPE_KEY);
}
+bool dns_type_may_wildcard(uint16_t type) {
+
+ /* The following records may not be expanded from wildcard RRsets */
+
+ if (dns_type_is_pseudo(type))
+ return false;
+
+ return !IN_SET(type,
+ DNS_TYPE_NSEC3,
+ DNS_TYPE_SOA,
+
+ /* Prohibited by https://tools.ietf.org/html/rfc4592#section-4.4 */
+ DNS_TYPE_DNAME);
+}
+
+bool dns_type_apex_only(uint16_t type) {
+
+ /* Returns true for all RR types that may only appear signed in a zone apex */
+
+ return IN_SET(type,
+ DNS_TYPE_SOA,
+ DNS_TYPE_NS, /* this one can appear elsewhere, too, but not signed */
+ DNS_TYPE_DNSKEY,
+ DNS_TYPE_NSEC3PARAM);
+}
+
bool dns_type_is_dnssec(uint16_t type) {
return IN_SET(type,
DNS_TYPE_DS,
@@ -157,6 +185,23 @@ bool dns_type_is_obsolete(uint16_t type) {
DNS_TYPE_NULL);
}
+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;
+ }
+}
+
const char *dns_class_to_string(uint16_t class) {
switch (class) {
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 45080fd243..60ff160383 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -131,6 +131,9 @@ bool dns_type_is_valid_rr(uint16_t type);
bool dns_type_may_redirect(uint16_t type);
bool dns_type_is_dnssec(uint16_t type);
bool dns_type_is_obsolete(uint16_t type);
+bool dns_type_may_wildcard(uint16_t type);
+bool dns_type_apex_only(uint16_t type);
+int dns_type_to_af(uint16_t t);
bool dns_class_is_pseudo(uint16_t class);
bool dns_class_is_valid_rr(uint16_t class);
diff --git a/src/resolve-host/resolve-host.c b/src/resolve/resolve-tool.c
index 2cabfeaefa..fdaeb8d926 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve/resolve-tool.c
@@ -64,10 +64,12 @@ static void print_source(uint64_t flags, usec_t rtt) {
fputs("\n-- Information acquired via", stdout);
if (flags != 0)
- printf(" protocol%s%s%s",
+ printf(" protocol%s%s%s%s%s",
flags & SD_RESOLVED_DNS ? " DNS" :"",
flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "");
+ flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
+ flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" : "",
+ flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : "");
assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
@@ -769,10 +771,26 @@ static int show_statistics(sd_bus *bus) {
uint64_t n_current_transactions, n_total_transactions,
cache_size, n_cache_hit, n_cache_miss,
n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
- int r;
+ int r, dnssec_supported;
assert(bus);
+ r = sd_bus_get_property_trivial(bus,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "DNSSECSupported",
+ &error,
+ 'b',
+ &dnssec_supported);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r));
+
+ printf("DNSSEC supported by current servers: %s%s%s\n\n",
+ ansi_highlight(),
+ yes_no(dnssec_supported),
+ ansi_normal());
+
r = sd_bus_get_property(bus,
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
@@ -849,11 +867,11 @@ static int show_statistics(sd_bus *bus) {
if (r < 0)
return bus_log_parse_error(r);
- printf("\n%sDNSSEC%s\n"
- " Secure RRsets: %" PRIu64 "\n"
- " Insecure RRsets: %" PRIu64 "\n"
- " Bogus RRsets: %" PRIu64 "\n"
- "Indeterminate RRsets: %" PRIu64 "\n",
+ printf("\n%sDNSSEC Verdicts%s\n"
+ " Secure: %" PRIu64 "\n"
+ " Insecure: %" PRIu64 "\n"
+ " Bogus: %" PRIu64 "\n"
+ " Indeterminate: %" PRIu64 "\n",
ansi_highlight(),
ansi_normal(),
n_dnssec_secure,
@@ -911,12 +929,12 @@ static void help_dns_classes(void) {
static void help(void) {
printf("%s [OPTIONS...] NAME...\n"
"%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
- "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+ "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -4 Resolve IPv4 addresses\n"
" -6 Resolve IPv6 addresses\n"
- " -i INTERFACE Look on interface\n"
+ " -i --interface=INTERFACE Look on interface\n"
" -p --protocol=PROTOCOL Look via protocol\n"
" -t --type=TYPE Query RR with DNS type\n"
" -c --class=CLASS Query RR with DNS class\n"
@@ -925,7 +943,7 @@ static void help(void) {
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
" --cname=BOOL Do [not] follow CNAME redirects\n"
" --search=BOOL Do [not] use search domains\n"
- " --legend=BOOL Do [not] print column headers\n"
+ " --legend=BOOL Do [not] print column headers and meta information\n"
" --statistics Show resolver statistics\n"
" --reset-statistics Reset resolver statistics\n"
, program_invocation_short_name, program_invocation_short_name);
@@ -950,6 +968,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "type", required_argument, NULL, 't' },
{ "class", required_argument, NULL, 'c' },
{ "legend", required_argument, NULL, ARG_LEGEND },
+ { "interface", required_argument, NULL, 'i' },
{ "protocol", required_argument, NULL, 'p' },
{ "cname", required_argument, NULL, ARG_CNAME },
{ "service", no_argument, NULL, ARG_SERVICE },
@@ -1156,7 +1175,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_HOST:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1184,7 +1203,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_RECORD:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1213,7 +1232,7 @@ int main(int argc, char **argv) {
else if (argc == optind + 3)
r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
else {
- log_error("Too many arguments");
+ log_error("Too many arguments.");
r = -EINVAL;
goto finish;
}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 41f90dedfd..834ae837de 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -25,20 +25,9 @@
#include "dns-domain.h"
#include "resolved-bus.h"
#include "resolved-def.h"
+#include "resolved-link-bus.h"
static int reply_query_state(DnsQuery *q) {
- _cleanup_free_ char *ip = NULL;
- const char *name;
- int r;
-
- if (q->request_address_valid) {
- r = in_addr_to_string(q->request_family, &q->request_address, &ip);
- if (r < 0)
- return r;
-
- name = ip;
- } else
- name = dns_question_first_name(q->question);
switch (q->state) {
@@ -54,8 +43,8 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_INVALID_REPLY:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
- case DNS_TRANSACTION_RESOURCES:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
+ case DNS_TRANSACTION_ERRNO:
+ return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m");
case DNS_TRANSACTION_ABORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
@@ -70,14 +59,22 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
+ case DNS_TRANSACTION_NETWORK_DOWN:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down");
+
+ case DNS_TRANSACTION_NOT_FOUND:
+ /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
+ * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
+ return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
+
case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
- sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
+ sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
else {
const char *rc, *n;
- char p[3]; /* the rcode is 4 bits long */
+ char p[DECIMAL_STR_MAX(q->answer_rcode)];
rc = dns_rcode_to_string(q->answer_rcode);
if (!rc) {
@@ -86,7 +83,7 @@ static int reply_query_state(DnsQuery *q) {
}
n = strjoina(_BUS_ERROR_DNS, rc);
- sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
+ sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc);
}
return sd_bus_reply_method_error(q->request, &error);
@@ -144,8 +141,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsResourceRecord *rr;
unsigned added = 0;
- int r;
+ int ifindex, r;
assert(q);
@@ -156,7 +154,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
@@ -172,30 +170,29 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
- int ifindex;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ DnsQuestion *question;
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = append_address(reply, rr, ifindex);
- if (r < 0)
- goto finish;
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
+ r = append_address(reply, rr, ifindex);
+ if (r < 0)
+ goto finish;
- added ++;
- }
+ if (!canonical)
+ canonical = dns_resource_record_ref(rr);
+
+ added ++;
}
if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
@@ -239,7 +236,7 @@ static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus
}
static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
Manager *m = userdata;
const char *hostname;
int family, ifindex;
@@ -269,11 +266,15 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- r = dns_question_new_address(&question, family, hostname);
+ r = dns_question_new_address(&question_utf8, family, hostname, false);
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags);
+ r = dns_question_new_address(&question_idna, family, hostname, true);
+ if (r < 0)
+ return r;
+
+ r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags);
if (r < 0)
return r;
@@ -298,6 +299,7 @@ fail:
static void bus_method_resolve_address_complete(DnsQuery *q) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsQuestion *question;
DnsResourceRecord *rr;
unsigned added = 0;
int ifindex, r;
@@ -311,7 +313,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
@@ -327,20 +329,20 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(q->question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
- if (r < 0)
- goto finish;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- added ++;
- }
+ r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
+ if (r < 0)
+ goto finish;
+
+ added ++;
}
if (added <= 0) {
@@ -411,7 +413,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+ r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
@@ -465,7 +467,10 @@ static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int i
static void bus_method_resolve_record_complete(DnsQuery *q) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsResourceRecord *rr;
+ DnsQuestion *question;
unsigned added = 0;
+ int ifindex;
int r;
assert(q);
@@ -477,7 +482,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
@@ -493,27 +498,24 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
- int ifindex;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(q->question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = bus_message_append_rr(reply, rr, ifindex);
- if (r < 0)
- goto finish;
+ r = bus_message_append_rr(reply, rr, ifindex);
+ if (r < 0)
+ goto finish;
- added ++;
- }
+ added ++;
}
if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
@@ -582,7 +584,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+ r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
@@ -622,13 +624,16 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
* record for the SRV record */
LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
DnsResourceRecord *zz;
+ DnsQuestion *question;
if (aux->state != DNS_TRANSACTION_SUCCESS)
continue;
if (aux->auxiliary_result != 0)
continue;
- r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
+ question = dns_query_question_for_protocol(aux, aux->answer_protocol);
+
+ r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
if (r < 0)
return r;
if (r == 0)
@@ -636,7 +641,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
DNS_ANSWER_FOREACH(zz, aux->answer) {
- r = dns_question_matches_rr(aux->question, zz, NULL);
+ r = dns_question_matches_rr(question, zz, NULL);
if (r < 0)
return r;
if (r == 0)
@@ -673,6 +678,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
DnsResourceRecord *zz;
+ DnsQuestion *question;
int ifindex;
if (aux->state != DNS_TRANSACTION_SUCCESS)
@@ -680,7 +686,9 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
if (aux->auxiliary_result != 0)
continue;
- r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name);
+ question = dns_query_question_for_protocol(aux, aux->answer_protocol);
+
+ r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
if (r < 0)
return r;
if (r == 0)
@@ -688,7 +696,7 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr)
DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
- r = dns_question_matches_rr(aux->question, zz, NULL);
+ r = dns_question_matches_rr(question, zz, NULL);
if (r < 0)
return r;
if (r == 0)
@@ -746,8 +754,10 @@ static void resolve_service_all_complete(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
+ DnsQuestion *question;
+ DnsResourceRecord *rr;
+ unsigned added = 0;
DnsQuery *aux;
- unsigned added = false;
int r;
assert(q);
@@ -789,7 +799,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
assert(bad->auxiliary_result != 0);
if (bad->auxiliary_result == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad));
goto finish;
}
@@ -810,31 +820,28 @@ static void resolve_service_all_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
-
- DNS_ANSWER_FOREACH(rr, q->answer) {
- r = dns_question_matches_rr(q->question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = append_srv(q, reply, rr);
- if (r < 0)
- goto finish;
- if (r == 0) /* not an SRV record */
- continue;
+ r = append_srv(q, reply, rr);
+ if (r < 0)
+ goto finish;
+ if (r == 0) /* not an SRV record */
+ continue;
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
+ if (!canonical)
+ canonical = dns_resource_record_ref(rr);
- added++;
- }
+ added++;
}
if (added <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
@@ -846,20 +853,16 @@ static void resolve_service_all_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
-
- DNS_ANSWER_FOREACH(rr, q->answer) {
- r = dns_question_matches_rr(q->question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = append_txt(reply, rr);
- if (r < 0)
- goto finish;
- }
+ r = append_txt(reply, rr);
+ if (r < 0)
+ goto finish;
}
r = sd_bus_message_close_container(reply);
@@ -923,11 +926,11 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin
/* OK, we found an SRV record for the service. Let's resolve
* the hostname included in it */
- r = dns_question_new_address(&question, q->request_family, rr->srv.name);
+ r = dns_question_new_address(&question, q->request_family, rr->srv.name, false);
if (r < 0)
return r;
- r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
+ r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
@@ -961,8 +964,11 @@ fail:
}
static void bus_method_resolve_service_complete(DnsQuery *q) {
+ bool has_root_domain = false;
+ DnsResourceRecord *rr;
+ DnsQuestion *question;
unsigned found = 0;
- int r;
+ int ifindex, r;
assert(q);
@@ -973,7 +979,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
r = dns_query_process_cname(q);
if (r == -ELOOP) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
@@ -981,53 +987,48 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return;
- if (q->answer) {
- bool has_root_domain = false;
- DnsResourceRecord *rr;
- int ifindex;
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- r = dns_question_matches_rr(q->question, rr, NULL);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- if (rr->key->type != DNS_TYPE_SRV)
- continue;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(question, rr, NULL);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- if (dns_name_is_root(rr->srv.name)) {
- has_root_domain = true;
- continue;
- }
+ if (rr->key->type != DNS_TYPE_SRV)
+ continue;
- if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
- q->block_all_complete ++;
- r = resolve_service_hostname(q, rr, ifindex);
- q->block_all_complete --;
+ if (dns_name_is_root(rr->srv.name)) {
+ has_root_domain = true;
+ continue;
+ }
- if (r < 0)
- goto finish;
- }
+ if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
+ q->block_all_complete ++;
+ r = resolve_service_hostname(q, rr, ifindex);
+ q->block_all_complete --;
- found++;
+ if (r < 0)
+ goto finish;
}
- if (has_root_domain && found == 0) {
- /* If there's exactly one SRV RR and it uses
- * the root domain as host name, then the
- * service is explicitly not offered on the
- * domain. Report this as a recognizable
- * error. See RFC 2782, Section "Usage
- * Rules". */
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_question_first_name(q->question));
- goto finish;
- }
+ found++;
+ }
+ if (has_root_domain && found <= 0) {
+ /* If there's exactly one SRV RR and it uses
+ * the root domain as host name, then the
+ * service is explicitly not offered on the
+ * domain. Report this as a recognizable
+ * error. See RFC 2782, Section "Usage
+ * Rules". */
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q));
+ goto finish;
}
if (found <= 0) {
- r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question));
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
@@ -1045,8 +1046,8 @@ finish:
}
static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
- const char *name, *type, *domain, *joined;
+ _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
+ const char *name, *type, *domain;
_cleanup_free_ char *n = NULL;
Manager *m = userdata;
int family, ifindex;
@@ -1068,10 +1069,8 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
if (isempty(name))
name = NULL;
- else {
- if (!dns_service_name_is_valid(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
- }
+ else if (!dns_service_name_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
if (isempty(type))
type = NULL;
@@ -1091,23 +1090,15 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- if (type) {
- /* If the type is specified, we generate the full domain name to look up ourselves */
- r = dns_service_join(name, type, domain, &n);
- if (r < 0)
- return r;
-
- joined = n;
- } else
- /* If no type is specified, we assume the domain
- * contains the full domain name to lookup already */
- joined = domain;
+ r = dns_question_new_service(&question_utf8, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), false);
+ if (r < 0)
+ return r;
- r = dns_question_new_service(&question, joined, !(flags & SD_RESOLVED_NO_TXT));
+ r = dns_question_new_service(&question_idna, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), true);
if (r < 0)
return r;
- r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
+ r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
@@ -1130,17 +1121,23 @@ fail:
return r;
}
-static int append_dns_server(sd_bus_message *reply, DnsServer *s) {
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
int r;
assert(reply);
assert(s);
- r = sd_bus_message_open_container(reply, 'r', "iiay");
+ r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family);
+ if (with_ifindex) {
+ r = sd_bus_message_append(reply, "i", s->link ? s->link->ifindex : 0);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_append(reply, "i", s->family);
if (r < 0)
return r;
@@ -1175,7 +1172,7 @@ static int bus_property_get_dns_servers(
return r;
LIST_FOREACH(servers, s, m->dns_servers) {
- r = append_dns_server(reply, s);
+ r = bus_dns_server_append(reply, s, true);
if (r < 0)
return r;
@@ -1184,7 +1181,7 @@ static int bus_property_get_dns_servers(
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(servers, s, l->dns_servers) {
- r = append_dns_server(reply, s);
+ r = bus_dns_server_append(reply, s, true);
if (r < 0)
return r;
c++;
@@ -1193,7 +1190,7 @@ static int bus_property_get_dns_servers(
if (c == 0) {
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
- r = append_dns_server(reply, s);
+ r = bus_dns_server_append(reply, s, true);
if (r < 0)
return r;
}
@@ -1220,19 +1217,19 @@ static int bus_property_get_search_domains(
assert(reply);
assert(m);
- r = sd_bus_message_open_container(reply, 'a', "(is)");
+ r = sd_bus_message_open_container(reply, 'a', "(isb)");
if (r < 0)
return r;
LIST_FOREACH(domains, d, m->search_domains) {
- r = sd_bus_message_append(reply, "(is)", 0, d->name);
+ r = sd_bus_message_append(reply, "(isb)", 0, d->name, d->route_only);
if (r < 0)
return r;
}
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(domains, d, l->search_domains) {
- r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
+ r = sd_bus_message_append(reply, "(isb)", l->ifindex, d->name, d->route_only);
if (r < 0)
return r;
}
@@ -1300,10 +1297,27 @@ static int bus_property_get_dnssec_statistics(
assert(m);
return sd_bus_message_append(reply, "(tttt)",
- (uint64_t) m->n_dnssec_secure,
- (uint64_t) m->n_dnssec_insecure,
- (uint64_t) m->n_dnssec_bogus,
- (uint64_t) m->n_dnssec_indeterminate);
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_SECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INDETERMINATE]);
+}
+
+static int bus_property_get_dnssec_supported(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+
+ assert(reply);
+ assert(m);
+
+ return sd_bus_message_append(reply, "b", manager_dnssec_supported(m));
}
static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1317,25 +1331,145 @@ static int bus_method_reset_statistics(sd_bus_message *message, void *userdata,
s->cache.n_hit = s->cache.n_miss = 0;
m->n_transactions_total = 0;
- m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
+ zero(m->n_dnssec_verdict);
return sd_bus_reply_method_return(message, NULL);
}
+static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
+ Link *l;
+
+ assert(m);
+ assert(ret);
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ if (!l)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
+
+ *ret = l;
+ return 0;
+}
+
+static int get_unmanaged_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
+ Link *l;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = get_any_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ if (l->flags & IFF_LOOPBACK)
+ return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name);
+ if (l->is_managed)
+ return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name);
+
+ *ret = l;
+ return 0;
+}
+
+static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
+ int ifindex, r;
+ Link *l;
+
+ assert(m);
+ assert(message);
+ assert(handler);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ r = get_unmanaged_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ return handler(message, l, error);
+}
+
+static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
+}
+
+static int bus_method_set_link_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_search_domains, error);
+}
+
+static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_llmnr, error);
+}
+
+static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_mdns, error);
+}
+
+static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec, error);
+}
+
+static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error);
+}
+
+static int bus_method_revert_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_revert, error);
+}
+
+static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *p = NULL;
+ Manager *m = userdata;
+ int r, ifindex;
+ Link *l;
+
+ assert(message);
+ assert(m);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ r = get_any_link(m, ifindex, &l, error);
+ if (r < 0)
+ return r;
+
+ p = link_bus_path(l);
+ if (!p)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(message, "o", p);
+}
+
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
- SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0),
- SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0),
+ SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0),
+ SD_BUS_PROPERTY("SearchDomains", "a(isb)", bus_property_get_search_domains, 0, 0),
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
+ SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
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_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
+ SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
+ SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_search_domains, 0),
+ SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
+ SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
+ SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0),
+ SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0),
+ SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0),
+
SD_BUS_VTABLE_END,
};
@@ -1401,6 +1535,14 @@ int manager_connect_bus(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
+ r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register link objects: %m");
+
+ r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register link enumerator: %m");
+
r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h
index 1e72891178..1ee57ba43d 100644
--- a/src/resolve/resolved-bus.h
+++ b/src/resolve/resolved-bus.h
@@ -24,3 +24,4 @@
#include "resolved-manager.h"
int manager_connect_bus(Manager *m);
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 88df7534c4..6d8c35164e 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -80,20 +80,34 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con
int manager_add_search_domain_by_string(Manager *m, const char *domain) {
DnsSearchDomain *d;
+ bool route_only;
int r;
assert(m);
assert(domain);
+ route_only = *domain == '~';
+ if (route_only)
+ domain++;
+
+ if (dns_name_is_root(domain) || streq(domain, "*")) {
+ route_only = true;
+ domain = ".";
+ }
+
r = dns_search_domain_find(m->search_domains, domain, &d);
if (r < 0)
return r;
- if (r > 0) {
+ if (r > 0)
dns_search_domain_move_back_and_unmark(d);
- return 0;
+ else {
+ r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+ if (r < 0)
+ return r;
}
- return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+ d->route_only = route_only;
+ return 0;
}
int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index b50558e280..f74e440531 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -320,6 +320,33 @@ int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
return false;
}
+int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
+ DnsResourceRecord *rr;
+ int r;
+
+ /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
+
+ DNS_ANSWER_FOREACH(rr, answer) {
+ const char *p;
+
+ if (rr->key->type != DNS_TYPE_NSEC3)
+ continue;
+
+ p = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_parent(&p);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_equal(p, zone);
+ if (r != 0)
+ return r;
+ }
+
+ return false;
+}
+
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
DnsResourceRecord *rr, *soa = NULL;
DnsAnswerFlags rr_flags, soa_flags = 0;
@@ -794,3 +821,40 @@ void dns_answer_dump(DnsAnswer *answer, FILE *f) {
fputc('\n', f);
}
}
+
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
+ DnsResourceRecord *rr;
+ int r;
+
+ assert(cname);
+
+ /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
+ * synthesized from it */
+
+ if (cname->key->type != DNS_TYPE_CNAME)
+ return 0;
+
+ DNS_ANSWER_FOREACH(rr, a) {
+ _cleanup_free_ char *n = NULL;
+
+ if (rr->key->type != DNS_TYPE_DNAME)
+ continue;
+ if (rr->key->class != cname->key->class)
+ continue;
+
+ r = dns_name_change_suffix(cname->cname.name, rr->dname.name, DNS_RESOURCE_KEY_NAME(rr->key), &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ r = dns_name_equal(n, DNS_RESOURCE_KEY_NAME(cname->key));
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 1;
+
+ }
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 715e487d94..1875fd6136 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -64,6 +64,7 @@ int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
+int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
@@ -82,6 +83,8 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
+
static inline unsigned dns_answer_size(DnsAnswer *a) {
return a ? a->n_rrs : 0;
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 301f383809..9267b67f79 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -247,6 +247,19 @@ static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
first = hashmap_get(c->by_key, i->key);
if (first) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
+
+ /* Keep a reference to the original key, while we manipulate the list. */
+ k = dns_resource_key_ref(first->key);
+
+ /* Now, try to reduce the number of keys we keep */
+ dns_resource_key_reduce(&first->key, &i->key);
+
+ if (first->rr)
+ dns_resource_key_reduce(&first->rr->key, &i->key);
+ if (i->rr)
+ dns_resource_key_reduce(&i->rr->key, &i->key);
+
LIST_PREPEND(by_key, first, i);
assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
} else {
@@ -566,6 +579,28 @@ static void dns_cache_remove_previous(
}
}
+static bool rr_eligible(DnsResourceRecord *rr) {
+ assert(rr);
+
+ /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
+ * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
+ * existence from any cached NSEC/NSEC3, but that should be fine. */
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_NSEC:
+ return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
+
+ case DNS_TYPE_NSEC3:
+ return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
+
+ default:
+ return true;
+ }
+}
+
int dns_cache_put(
DnsCache *c,
DnsResourceKey *key,
@@ -622,6 +657,12 @@ int dns_cache_put(
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
continue;
+ r = rr_eligible(rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
r = dns_cache_put_positive(
c,
rr,
@@ -822,7 +863,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
have_non_authenticated = true;
}
- if (nsec && key->type != DNS_TYPE_NSEC) {
+ if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
+ /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
+ * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
+
if (log_get_max_level() >= LOG_DEBUG) {
r = dns_resource_key_to_string(key, &key_str);
if (r < 0)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 43fcbe1460..76c801cce8 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -28,26 +28,6 @@
#include "resolved-dns-packet.h"
#include "string-table.h"
-/* Open question:
- *
- * How does the DNSSEC canonical form of a hostname with a label
- * containing a dot look like, the way DNS-SD does it?
- *
- * TODO:
- *
- * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
- * - multi-label zone compatibility
- * - cname/dname compatibility
- * - nxdomain on qname
- * - bus calls to override DNSEC setting per interface
- * - log all DNSSEC downgrades
- * - enable by default
- *
- * - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies)
- * - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone)
- * - RFC 6840, Section 4.3 (check for CNAME on NSEC too)
- * */
-
#define VERIFY_RRS_MAX 256
#define MAX_KEY_SIZE (32*1024)
@@ -430,6 +410,57 @@ static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
gcry_md_write(md, &v, sizeof(v));
}
+static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
+ int n_key_labels, n_signer_labels;
+ const char *name;
+ int r;
+
+ /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
+ * .n_skip_labels_signer fields so that we can use them later on. */
+
+ assert(rrsig);
+ assert(rrsig->key->type == DNS_TYPE_RRSIG);
+
+ /* Check if this RRSIG RR is already prepared */
+ if (rrsig->n_skip_labels_source != (unsigned) -1)
+ return 0;
+
+ if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
+ return -EINVAL;
+
+ name = DNS_RESOURCE_KEY_NAME(rrsig->key);
+
+ n_key_labels = dns_name_count_labels(name);
+ if (n_key_labels < 0)
+ return n_key_labels;
+ if (rrsig->rrsig.labels > n_key_labels)
+ return -EINVAL;
+
+ n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
+ if (n_signer_labels < 0)
+ return n_signer_labels;
+ if (n_signer_labels > rrsig->rrsig.labels)
+ return -EINVAL;
+
+ r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ /* Check if the signer is really a suffix of us */
+ r = dns_name_equal(name, rrsig->rrsig.signer);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
+ rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
+
+ return 0;
+}
+
static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
usec_t expiration, inception, skew;
@@ -442,8 +473,9 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
inception = rrsig->rrsig.inception * USEC_PER_SEC;
+ /* Consider inverted validity intervals as expired */
if (inception > expiration)
- return -EKEYREJECTED;
+ return true;
/* Permit a certain amount of clock skew of 10% of the valid
* time range. This takes inspiration from unbound's
@@ -498,6 +530,35 @@ static int algorithm_to_gcrypt_md(uint8_t algorithm) {
}
}
+static void dnssec_fix_rrset_ttl(
+ DnsResourceRecord *list[],
+ unsigned n,
+ DnsResourceRecord *rrsig,
+ usec_t realtime) {
+
+ unsigned k;
+
+ assert(list);
+ assert(n > 0);
+ assert(rrsig);
+
+ for (k = 0; k < n; k++) {
+ DnsResourceRecord *rr = list[k];
+
+ /* Pick the TTL as the minimum of the RR's TTL, the
+ * RR's original TTL according to the RRSIG and the
+ * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
+ rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
+ rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+
+ /* Copy over information about the signer and wildcard source of synthesis */
+ rr->n_skip_labels_source = rrsig->n_skip_labels_source;
+ rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
+ }
+
+ rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+}
+
int dnssec_verify_rrset(
DnsAnswer *a,
const DnsResourceKey *key,
@@ -507,13 +568,14 @@ int dnssec_verify_rrset(
DnssecResult *result) {
uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
- size_t hash_size;
- void *hash;
DnsResourceRecord **list, *rr;
+ const char *source, *name;
gcry_md_hd_t md = NULL;
int r, md_algorithm;
- bool wildcard = false;
size_t k, n = 0;
+ size_t hash_size;
+ void *hash;
+ bool wildcard;
assert(key);
assert(rrsig);
@@ -534,6 +596,14 @@ int dnssec_verify_rrset(
if (md_algorithm < 0)
return md_algorithm;
+ r = dnssec_rrsig_prepare(rrsig);
+ if (r == -EINVAL) {
+ *result = DNSSEC_INVALID;
+ return r;
+ }
+ if (r < 0)
+ return r;
+
r = dnssec_rrsig_expired(rrsig, realtime);
if (r < 0)
return r;
@@ -542,6 +612,52 @@ int dnssec_verify_rrset(
return 0;
}
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
+ if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
+ r = dns_name_equal(rrsig->rrsig.signer, name);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ }
+
+ /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
+ if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
+ r = dns_name_equal(rrsig->rrsig.signer, name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ }
+
+ /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
+ r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
+ if (r < 0)
+ return r;
+ if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
+ /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
+ *result = DNSSEC_INVALID;
+ return 0;
+ }
+ if (r == 1) {
+ /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
+ * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
+ r = dns_name_startswith(name, "*");
+ if (r < 0)
+ return r;
+ if (r > 0)
+ source = name;
+
+ wildcard = r == 0;
+ } else
+ wildcard = r > 0;
+
/* Collect all relevant RRs in a single array, so that we can look at the RRset */
list = newa(DnsResourceRecord *, dns_answer_size(a));
@@ -592,22 +708,19 @@ int dnssec_verify_rrset(
goto finish;
gcry_md_write(md, wire_format_name, r);
+ /* Convert the source of synthesis into wire format */
+ r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
+ if (r < 0)
+ goto finish;
+
for (k = 0; k < n; k++) {
- const char *suffix;
size_t l;
+
rr = list[k];
- r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
- if (r < 0)
- goto finish;
- if (r > 0) /* This is a wildcard! */ {
+ /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
+ if (wildcard)
gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
- wildcard = true;
- }
-
- r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
- if (r < 0)
- goto finish;
gcry_md_write(md, wire_format_name, r);
md_add_uint16(md, rr->key->type);
@@ -654,12 +767,17 @@ int dnssec_verify_rrset(
if (r < 0)
goto finish;
- if (!r)
+ /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
+ if (r > 0)
+ dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
+
+ if (r == 0)
*result = DNSSEC_INVALID;
else if (wildcard)
*result = DNSSEC_VALIDATED_WILDCARD;
else
*result = DNSSEC_VALIDATED;
+
r = 0;
finish:
@@ -698,8 +816,6 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
}
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
- int r;
-
assert(key);
assert(rrsig);
@@ -712,45 +828,9 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
if (rrsig->rrsig.type_covered != key->type)
return 0;
- /* Make sure signer is a parent of the RRset */
- r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
- if (r <= 0)
- return r;
-
- /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
- r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
- if (r < 0)
- return r;
- if (r < rrsig->rrsig.labels)
- return 0;
-
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
}
-static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
- DnsResourceRecord *rr;
- int r;
-
- assert(key);
- assert(rrsig);
-
- DNS_ANSWER_FOREACH(rr, a) {
- r = dns_resource_key_equal(key, rr->key);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- /* Pick the TTL as the minimum of the RR's TTL, the
- * RR's original TTL according to the RRSIG and the
- * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
- rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
- rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
- }
-
- return 0;
-}
-
int dnssec_verify_rrset_search(
DnsAnswer *a,
const DnsResourceKey *key,
@@ -820,10 +900,6 @@ int dnssec_verify_rrset_search(
case DNSSEC_VALIDATED_WILDCARD:
/* Yay, the RR has been validated,
* return immediately, but fix up the expiry */
- r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
- if (r < 0)
- return r;
-
if (ret_rrsig)
*ret_rrsig = rrsig;
@@ -911,16 +987,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
return r;
if (r == 0)
break;
- if (r > 0) {
- int k;
-
- /* DNSSEC validation is always done on the ASCII version of the label */
- k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
- }
if (buffer_max < (size_t) r + 2)
return -ENOBUFS;
@@ -974,7 +1040,7 @@ static int digest_to_gcrypt_md(uint8_t algorithm) {
}
}
-int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
gcry_md_hd_t md = NULL;
size_t hash_size;
@@ -1044,7 +1110,7 @@ finish:
return r;
}
-int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
DnsResourceRecord *ds;
DnsAnswerFlags flags;
int r;
@@ -1061,7 +1127,6 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
if (ds->key->type != DNS_TYPE_DS)
continue;
-
if (ds->key->class != dnskey->key->class)
continue;
@@ -1071,9 +1136,9 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
if (r == 0)
continue;
- r = dnssec_verify_dnskey(dnskey, ds, false);
- if (r == -EKEYREJECTED)
- return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
+ r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
+ if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
+ return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
if (r < 0)
return r;
if (r > 0)
@@ -1190,6 +1255,14 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
return 0;
+ /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
+ * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
+ if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1)
+ return 0;
+ /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
+ if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1)
+ return 0;
+
if (!nsec3)
return 1;
@@ -1223,6 +1296,7 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
if (r == 0)
return 0;
+ /* Make sure both have the same parent */
return dns_name_equal(a, b);
}
@@ -1274,8 +1348,8 @@ static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain
* name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
* to conclude anything we indicate this by returning NO_RR. */
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
- _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
- const char *zone, *p, *pp = NULL;
+ _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
+ const char *zone, *p, *pp = NULL, *wildcard;
DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
DnsAnswerFlags flags;
int hashed_size, r;
@@ -1370,19 +1444,20 @@ found_zone:
found_closest_encloser:
/* We found a closest encloser in 'p'; next closer is 'pp' */
- /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
- return -EBADMSG;
-
- /* Ensure that this data is from the delegated domain
- * (i.e. originates from the "lower" DNS server), and isn't
- * just glue records (i.e. doesn't originate from the "upper"
- * DNS server). */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
- !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
-
if (!pp) {
+ /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
+ * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
+ * appropriately set. */
+
+ if (key->type == DNS_TYPE_DS) {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ } else {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ }
+
/* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
*result = DNSSEC_NSEC_FOUND;
@@ -1399,12 +1474,21 @@ found_closest_encloser:
return 0;
}
- /* Prove that there is no next closer and whether or not there is a wildcard domain. */
+ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
+ return -EBADMSG;
- wildcard = strappend("*.", p);
- if (!wildcard)
- return -ENOMEM;
+ /* Ensure that this data is from the delegated domain
+ * (i.e. originates from the "lower" DNS server), and isn't
+ * just glue records (i.e. doesn't originate from the "upper"
+ * DNS server). */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ /* Prove that there is no next closer and whether or not there is a wildcard domain. */
+
+ wildcard = strjoina("*.", p);
r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
if (r < 0)
return r;
@@ -1517,10 +1601,158 @@ found_closest_encloser:
return 0;
}
+static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
+ char label[DNS_LABEL_MAX];
+ const char *n;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */
+
+ if (rr->n_skip_labels_source != 1)
+ return 0;
+
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_label_unescape(&n, label, sizeof(label));
+ if (r <= 0)
+ return r;
+ if (r != 1 || label[0] != '*')
+ return 0;
+
+ return dns_name_endswith(name, n);
+}
+
+static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
+ const char *nn, *common_suffix;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
+ *
+ * A couple of examples:
+ *
+ * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
+ * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
+ * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
+ */
+
+ /* First, determine parent of next domain. */
+ nn = rr->nsec.next_domain_name;
+ r = dns_name_parent(&nn);
+ if (r <= 0)
+ return r;
+
+ /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
+ * anything at all. */
+ r = dns_name_endswith(nn, name);
+ if (r <= 0)
+ return r;
+
+ /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ return dns_name_endswith(name, common_suffix);
+}
+
+static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) {
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether this NSEC originates to the parent zone or the child zone. */
+
+ r = dns_name_parent(&name);
+ if (r <= 0)
+ return r;
+
+ r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key));
+ if (r <= 0)
+ return r;
+
+ /* DNAME, and NS without SOA is an indication for a delegation. */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME))
+ return 1;
+
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ return 1;
+
+ return 0;
+}
+
+static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
+ const char *common_suffix, *p;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */
+
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ p = name;
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ r = dns_name_equal(name, common_suffix);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ break;
+ }
+
+ /* p is now the "Next Closer". */
+
+ return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name);
+}
+
+static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
+ const char *common_suffix, *wc;
+ int r;
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_NSEC);
+
+ /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
+ * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
+ * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
+ *
+ * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
+ * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
+ * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either...
+ */
+
+ r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
+ if (r < 0)
+ return r;
+
+ /* If the common suffix is not shared by the name we are interested in, it has nothing to say for us. */
+ r = dns_name_endswith(name, common_suffix);
+ if (r <= 0)
+ return r;
+
+ wc = strjoina("*.", common_suffix, NULL);
+ return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name);
+}
+
int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
- DnsResourceRecord *rr;
- bool have_nsec3 = false;
+ bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
+ DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
DnsAnswerFlags flags;
+ const char *name;
int r;
assert(key);
@@ -1528,55 +1760,119 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
/* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
+ name = DNS_RESOURCE_KEY_NAME(key);
+
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
if (rr->key->class != key->class)
continue;
- switch (rr->key->type) {
+ have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
- case DNS_TYPE_NSEC:
+ if (rr->key->type != DNS_TYPE_NSEC)
+ continue;
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+ /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */
+ r = dns_resource_record_is_synthetic(rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ /* Check if this is a direct match. If so, we have encountered a NODATA case */
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* If it's not a direct match, maybe it's a wild card match? */
+ r = dnssec_nsec_wildcard_equal(rr, name);
if (r < 0)
return r;
- if (r > 0) {
- if (bitmap_isset(rr->nsec.types, key->type))
- *result = DNSSEC_NSEC_FOUND;
- else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
- *result = DNSSEC_NSEC_CNAME;
- else
- *result = DNSSEC_NSEC_NODATA;
-
- if (authenticated)
- *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- if (ttl)
- *ttl = rr->ttl;
-
- return 0;
+ }
+ if (r > 0) {
+ if (key->type == DNS_TYPE_DS) {
+ /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
+ * we have a problem. For DS RRs we want the NSEC RR from the parent */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ continue;
+ } else {
+ /* For all RR types, ensure that if NS is set SOA is set too, so that we know
+ * we got the child's NSEC. */
+ if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
+ !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
+ continue;
}
- r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
- if (r < 0)
- return r;
- if (r > 0) {
- *result = DNSSEC_NSEC_NXDOMAIN;
+ if (bitmap_isset(rr->nsec.types, key->type))
+ *result = DNSSEC_NSEC_FOUND;
+ else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
+ *result = DNSSEC_NSEC_CNAME;
+ else
+ *result = DNSSEC_NSEC_NODATA;
- if (authenticated)
- *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
- if (ttl)
- *ttl = rr->ttl;
+ if (authenticated)
+ *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ if (ttl)
+ *ttl = rr->ttl;
- return 0;
- }
- break;
+ return 0;
+ }
- case DNS_TYPE_NSEC3:
- have_nsec3 = true;
- break;
+ /* Check if the name we are looking for is an empty non-terminal within the owner or next name
+ * of the NSEC RR. */
+ r = dnssec_nsec_in_path(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ *result = DNSSEC_NSEC_NODATA;
+
+ if (authenticated)
+ *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ if (ttl)
+ *ttl = rr->ttl;
+
+ return 0;
+ }
+
+ /* The following two "covering" checks, are not useful if the NSEC is from the parent */
+ r = dnssec_nsec_from_parent_zone(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ /* Check if this NSEC RR proves the absence of an explicit RR under this name */
+ r = dnssec_nsec_covers(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
+ covering_rr = rr;
+ covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+ }
+
+ /* Check if this NSEC RR proves the absence of a wildcard RR under this name */
+ r = dnssec_nsec_covers_wildcard(rr, name);
+ if (r < 0)
+ return r;
+ if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
+ wildcard_rr = rr;
+ wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
}
}
+ if (covering_rr && wildcard_rr) {
+ /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
+ * proved the NXDOMAIN case. */
+ *result = DNSSEC_NSEC_NXDOMAIN;
+
+ if (authenticated)
+ *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
+ if (ttl)
+ *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
+
+ return 0;
+ }
+
/* OK, this was not sufficient. Let's see if NSEC3 can help. */
if (have_nsec3)
return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
@@ -1586,7 +1882,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
return 0;
}
-int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated) {
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
DnsResourceRecord *rr;
DnsAnswerFlags flags;
int r;
@@ -1600,15 +1896,20 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
bool found = false;
- r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
- if (r < 0)
- return r;
- if (r == 0)
+ if (rr->key->type != type && type != DNS_TYPE_ANY)
continue;
switch (rr->key->type) {
case DNS_TYPE_NSEC:
+
+ /* We only care for NSEC RRs from the indicated zone */
+ r = dns_resource_record_is_signer(rr, zone);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
if (r < 0)
return r;
@@ -1619,6 +1920,13 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo
case DNS_TYPE_NSEC3: {
_cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
+ /* We only care for NSEC3 RRs from the indicated zone */
+ r = dns_resource_record_is_signer(rr, zone);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
r = nsec3_is_good(rr, NULL);
if (r < 0)
return r;
@@ -1667,6 +1975,145 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo
return 0;
}
+static int dnssec_test_positive_wildcard_nsec3(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *authenticated) {
+
+ const char *next_closer = NULL;
+ int r;
+
+ /* Run a positive NSEC3 wildcard proof. Specifically:
+ *
+ * A proof that the the "next closer" of the generating wildcard does not exist.
+ *
+ * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
+ * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
+ * exists for the NSEC3 RR and we are done.
+ *
+ * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
+ * c.d.e.f does not exist. */
+
+ for (;;) {
+ next_closer = name;
+ r = dns_name_parent(&name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ r = dns_name_equal(name, source);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ break;
+ }
+
+ return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
+}
+
+static int dnssec_test_positive_wildcard_nsec(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *_authenticated) {
+
+ bool authenticated = true;
+ int r;
+
+ /* Run a positive NSEC wildcard proof. Specifically:
+ *
+ * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
+ * a prefix of the synthesizing source "source" in the zone "zone".
+ *
+ * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
+ *
+ * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
+ * have to prove that none of the following exist:
+ *
+ * 1) a.b.c.d.e.f
+ * 2) *.b.c.d.e.f
+ * 3) b.c.d.e.f
+ * 4) *.c.d.e.f
+ * 5) c.d.e.f
+ *
+ */
+
+ for (;;) {
+ _cleanup_free_ char *wc = NULL;
+ bool a = false;
+
+ /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
+ * i.e between the owner name and the next name of an NSEC RR. */
+ r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
+ if (r <= 0)
+ return r;
+
+ authenticated = authenticated && a;
+
+ /* Strip one label off */
+ r = dns_name_parent(&name);
+ if (r <= 0)
+ return r;
+
+ /* Did we reach the source of synthesis? */
+ r = dns_name_equal(name, source);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Successful exit */
+ *_authenticated = authenticated;
+ return 1;
+ }
+
+ /* Safety check, that the source of synthesis is still our suffix */
+ r = dns_name_endswith(name, source);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ /* Replace the label we stripped off with an asterisk */
+ wc = strappend("*.", name);
+ if (!wc)
+ return -ENOMEM;
+
+ /* And check if the proof holds for the asterisk name, too */
+ r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
+ if (r <= 0)
+ return r;
+
+ authenticated = authenticated && a;
+ /* In the next iteration we'll check the non-asterisk-prefixed version */
+ }
+}
+
+int dnssec_test_positive_wildcard(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *authenticated) {
+
+ int r;
+
+ assert(name);
+ assert(source);
+ assert(zone);
+ assert(authenticated);
+
+ r = dns_answer_contains_zone_nsec3(answer, zone);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
+ else
+ return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
+}
+
static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_VALIDATED] = "validated",
[DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
@@ -1681,3 +2128,11 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
+
+static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
+ [DNSSEC_SECURE] = "secure",
+ [DNSSEC_INSECURE] = "insecure",
+ [DNSSEC_BOGUS] = "bogus",
+ [DNSSEC_INDETERMINATE] = "indeterminate",
+};
+DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
index 8a9bcf5b91..c99861b8e5 100644
--- a/src/resolve/resolved-dns-dnssec.h
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef enum DnssecMode DnssecMode;
typedef enum DnssecResult DnssecResult;
+typedef enum DnssecVerdict DnssecVerdict;
#include "dns-domain.h"
#include "resolved-dns-answer.h"
@@ -50,6 +50,16 @@ enum DnssecResult {
_DNSSEC_RESULT_INVALID = -1
};
+enum DnssecVerdict {
+ DNSSEC_SECURE,
+ DNSSEC_INSECURE,
+ DNSSEC_BOGUS,
+ DNSSEC_INDETERMINATE,
+
+ _DNSSEC_VERDICT_MAX,
+ _DNSSEC_VERDICT_INVALID = -1
+};
+
#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
/* The longest digest we'll ever generate, of all digest algorithms we support */
@@ -61,8 +71,8 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result, DnsResourceRecord **rrsig);
-int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke);
-int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke);
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key);
@@ -83,7 +93,13 @@ typedef enum DnssecNsecResult {
} DnssecNsecResult;
int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
-int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated);
+
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated);
+
+int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated);
const char* dnssec_result_to_string(DnssecResult m) _const_;
DnssecResult dnssec_result_from_string(const char *s) _pure_;
+
+const char* dnssec_verdict_to_string(DnssecVerdict m) _const_;
+DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index a8a8632491..032e719595 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -499,7 +499,6 @@ int dns_packet_append_name(
const char *z = name;
char label[DNS_LABEL_MAX];
size_t n = 0;
- int k;
if (allow_compression)
n = PTR_TO_SIZE(hashmap_get(p->names, name));
@@ -519,17 +518,6 @@ int dns_packet_append_name(
if (r < 0)
goto fail;
- if (p->protocol == DNS_PROTOCOL_DNS)
- k = dns_label_apply_idna(label, r, label, sizeof(label));
- else
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0) {
- r = k;
- goto fail;
- }
- if (k > 0)
- r = k;
-
r = dns_packet_append_label(p, label, r, canonical_candidate, &n);
if (r < 0)
goto fail;
@@ -1083,7 +1071,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
/* Let's calculate the actual data size and update the field */
rdlength = p->size - rdlength_offset - sizeof(uint16_t);
if (rdlength > 0xFFFF) {
- r = ENOSPC;
+ r = -ENOSPC;
goto fail;
}
@@ -2017,6 +2005,48 @@ fail:
return r;
}
+static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
+ const uint8_t* p;
+ bool found_dau_dhu_n3u = false;
+ size_t l;
+
+ /* Checks whether the specified OPT RR is well-formed and whether it contains RFC6975 data (which is not OK in
+ * a reply). */
+
+ assert(rr);
+ assert(rr->key->type == DNS_TYPE_OPT);
+
+ /* Check that the version is 0 */
+ if (((rr->ttl >> 16) & UINT32_C(0xFF)) != 0)
+ return false;
+
+ p = rr->opt.data;
+ l = rr->opt.size;
+ while (l > 0) {
+ uint16_t option_code, option_length;
+
+ /* At least four bytes for OPTION-CODE and OPTION-LENGTH are required */
+ if (l < 4U)
+ return false;
+
+ option_code = unaligned_read_be16(p);
+ option_length = unaligned_read_be16(p + 2);
+
+ if (l < option_length + 4U)
+ return false;
+
+ /* RFC 6975 DAU, DHU or N3U fields found. */
+ if (IN_SET(option_code, 5, 6, 7))
+ found_dau_dhu_n3u = true;
+
+ p += option_length + 4U;
+ l -= option_length + 4U;
+ }
+
+ *rfc6975 = found_dau_dhu_n3u;
+ return true;
+}
+
int dns_packet_extract(DnsPacket *p) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
@@ -2064,6 +2094,9 @@ int dns_packet_extract(DnsPacket *p) {
n = DNS_PACKET_RRCOUNT(p);
if (n > 0) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
+ bool bad_opt = false;
+
answer = dns_answer_new(n);
if (!answer) {
r = -ENOMEM;
@@ -2078,36 +2111,62 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
goto finish;
+ /* Try to reduce memory usage a bit */
+ if (previous)
+ dns_resource_key_reduce(&rr->key, &previous->key);
+
if (rr->key->type == DNS_TYPE_OPT) {
+ bool has_rfc6975;
+
+ if (p->opt || bad_opt) {
+ /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong
+ * with the server, and if one is valid we wouldn't know which one. */
+ log_debug("Multiple OPT RRs detected, ignoring all.");
+ bad_opt = true;
+ continue;
+ }
if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) {
- r = -EBADMSG;
- goto finish;
+ /* If the OPT RR qis not owned by the root domain, then it is bad, let's ignore
+ * it. */
+ log_debug("OPT RR is not owned by root domain, ignoring.");
+ bad_opt = true;
+ continue;
+ }
+
+ if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
+ /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint
+ * the EDNS implementation is borked, like the Belkin one is, hence ignore
+ * it. */
+ log_debug("OPT RR in wrong section, ignoring.");
+ bad_opt = true;
+ continue;
}
- /* Note that we accept the OPT RR in
- * any section, not just in the
- * additional section, as some routers
- * (Belkin!) blindly copy the OPT RR
- * from the query to the reply packet,
- * and don't get the section right. */
+ if (!opt_is_good(rr, &has_rfc6975)) {
+ log_debug("Malformed OPT RR, ignoring.");
+ bad_opt = true;
+ continue;
+ }
- /* Two OPT RRs? */
- if (p->opt) {
- r = -EBADMSG;
- goto finish;
+ if (has_rfc6975) {
+ /* If the OPT RR contains RFC6975 algorithm data, then this is indication that
+ * the server just copied the OPT it got from us (which contained that data)
+ * back into the reply. If so, then it doesn't properly support EDNS, as
+ * RFC6975 makes it very clear that the algorithm data should only be contained
+ * in questions, never in replies. Crappy Belkin routers copy the OPT data for
+ * example, hence let's detect this so that we downgrade early. */
+ log_debug("OPT RR contained RFC6975 data, ignoring.");
+ bad_opt = true;
+ continue;
}
p->opt = dns_resource_record_ref(rr);
} else {
- /* According to RFC 4795, section
- * 2.9. only the RRs from the Answer
- * section shall be cached. Hence mark
- * only those RRs as cacheable by
- * default, but not the ones from the
- * Additional or Authority
- * sections. */
+ /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be
+ * cached. Hence mark only those RRs as cacheable by default, but not the ones from the
+ * Additional or Authority sections. */
r = dns_answer_add(answer, rr, p->ifindex,
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
@@ -2115,7 +2174,15 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
goto finish;
}
+
+ /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
+ * that we only do this if we actually decided to keep the RR around. */
+ dns_resource_record_unref(previous);
+ previous = dns_resource_record_ref(rr);
}
+
+ if (bad_opt)
+ p->opt = dns_resource_record_unref(p->opt);
}
p->question = question;
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index 6821be73e4..c53431576b 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -247,7 +247,7 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_;
#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
#define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) })
-#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfb } })
+#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } })
static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
uint64_t f;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 1948d59fc4..a00851658e 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -24,6 +24,9 @@
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-query.h"
+#include "resolved-dns-synthesize.h"
+#include "resolved-etc-hosts.h"
+#include "string-util.h"
/* How long to wait for the query in total */
#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
@@ -90,17 +93,20 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
assert(c);
- if (c->search_domain && c->search_domain->linked) {
+ if (c->search_domain && c->search_domain->linked)
next = c->search_domain->domains_next;
+ else
+ next = dns_scope_get_search_domains(c->scope);
+ for (;;) {
if (!next) /* We hit the end of the list */
return 0;
- } else {
- next = dns_scope_get_search_domains(c->scope);
+ if (!next->route_only)
+ break;
- if (!next) /* OK, there's nothing. */
- return 0;
+ /* Skip over route-only domains */
+ next = next->domains_next;
}
dns_search_domain_unref(c->search_domain);
@@ -179,7 +185,7 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
assert(c);
if (c->error_code != 0)
- return DNS_TRANSACTION_RESOURCES;
+ return DNS_TRANSACTION_ERRNO;
SET_FOREACH(t, c->transactions, i) {
@@ -217,6 +223,7 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
}
static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
+ DnsQuestion *question;
DnsResourceKey *key;
int n = 0, r;
@@ -224,8 +231,10 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
dns_query_candidate_stop(c);
+ question = dns_query_question_for_protocol(c->query, c->scope->protocol);
+
/* Create one transaction per question key */
- DNS_QUESTION_FOREACH(key, c->query->question) {
+ DNS_QUESTION_FOREACH(key, question) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
if (c->search_domain) {
@@ -305,6 +314,26 @@ static void dns_query_stop(DnsQuery *q) {
dns_query_candidate_stop(c);
}
+static void dns_query_free_candidates(DnsQuery *q) {
+ assert(q);
+
+ while (q->candidates)
+ dns_query_candidate_free(q->candidates);
+}
+
+static void dns_query_reset_answer(DnsQuery *q) {
+ assert(q);
+
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = 0;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = 0;
+ q->answer_authenticated = false;
+ q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ q->answer_family = AF_UNSPEC;
+ q->answer_search_domain = dns_search_domain_unref(q->answer_search_domain);
+}
+
DnsQuery *dns_query_free(DnsQuery *q) {
if (!q)
return NULL;
@@ -318,16 +347,18 @@ DnsQuery *dns_query_free(DnsQuery *q) {
LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
}
- while (q->candidates)
- dns_query_candidate_free(q->candidates);
+ dns_query_free_candidates(q);
- dns_question_unref(q->question);
- dns_answer_unref(q->answer);
- dns_search_domain_unref(q->answer_search_domain);
+ dns_question_unref(q->question_idna);
+ dns_question_unref(q->question_utf8);
+
+ dns_query_reset_answer(q);
sd_bus_message_unref(q->request);
sd_bus_track_unref(q->bus_track);
+ free(q->request_address_string);
+
if (q->manager) {
LIST_REMOVE(queries, q->manager->dns_queries, q);
q->manager->n_dns_queries--;
@@ -338,17 +369,50 @@ DnsQuery *dns_query_free(DnsQuery *q) {
return NULL;
}
-int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) {
+int dns_query_new(
+ Manager *m,
+ DnsQuery **ret,
+ DnsQuestion *question_utf8,
+ DnsQuestion *question_idna,
+ int ifindex, uint64_t flags) {
+
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
- unsigned i;
+ DnsResourceKey *key;
+ bool good = false;
int r;
assert(m);
- assert(question);
- r = dns_question_is_valid_for_query(question);
+ if (dns_question_size(question_utf8) > 0) {
+ r = dns_question_is_valid_for_query(question_utf8);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ good = true;
+ }
+
+ /* If the IDNA and UTF8 questions are the same, merge their references */
+ r = dns_question_is_equal(question_idna, question_utf8);
if (r < 0)
return r;
+ if (r > 0)
+ question_idna = question_utf8;
+ else {
+ if (dns_question_size(question_idna) > 0) {
+ r = dns_question_is_valid_for_query(question_idna);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ good = true;
+ }
+ }
+
+ if (!good) /* don't allow empty queries */
+ return -EINVAL;
if (m->n_dns_queries >= QUERIES_MAX)
return -EBUSY;
@@ -357,20 +421,40 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
if (!q)
return -ENOMEM;
- q->question = dns_question_ref(question);
+ q->question_utf8 = dns_question_ref(question_utf8);
+ q->question_idna = dns_question_ref(question_idna);
q->ifindex = ifindex;
q->flags = flags;
- q->answer_family = AF_UNSPEC;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ q->answer_family = AF_UNSPEC;
+
+ /* First dump UTF8 question */
+ DNS_QUESTION_FOREACH(key, question_utf8) {
+ _cleanup_free_ char *p = NULL;
+
+ r = dns_resource_key_to_string(key, &p);
+ if (r < 0)
+ return r;
- for (i = 0; i < question->n_keys; i++) {
- _cleanup_free_ char *p;
+ log_debug("Looking up RR for %s.", strstrip(p));
+ }
+
+ /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */
+ DNS_QUESTION_FOREACH(key, question_idna) {
+ _cleanup_free_ char *p = NULL;
+
+ r = dns_question_contains(question_utf8, key);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
- r = dns_resource_key_to_string(question->keys[i], &p);
+ r = dns_resource_key_to_string(key, &p);
if (r < 0)
return r;
- log_debug("Looking up RR for %s", p);
+ log_debug("Looking up IDNA RR for %s.", strstrip(p));
}
LIST_PREPEND(queries, m->dns_queries, q);
@@ -446,7 +530,7 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
/* If this a single-label domain on DNS, we might append a suitable search domain first. */
if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
- r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
+ r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna));
if (r < 0)
goto fail;
if (r > 0) {
@@ -469,413 +553,73 @@ fail:
return r;
}
-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 and we
- * only return localhost RRs, we synthesize the answer for
- * localhost. */
-
- if (ifindex > 0)
- return ifindex;
-
- return LOOPBACK_IFINDEX;
-}
-
-static int SYNTHESIZE_FAMILY(uint64_t flags) {
-
- /* Picks an address family depending on set flags. This is
- * purely for synthesized answers, where the family we return
- * for the reply should match what was requested in the
- * question, even though we are synthesizing the answer
- * here. */
-
- if (!(flags & SD_RESOLVED_DNS)) {
- if (flags & SD_RESOLVED_LLMNR_IPV4)
- return AF_INET;
- if (flags & SD_RESOLVED_LLMNR_IPV6)
- return AF_INET6;
- }
-
- return AF_UNSPEC;
-}
-
-static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
-
- /* Similar as SYNTHESIZE_FAMILY() but does this for the
- * protocol. If resolving via DNS was requested, we claim it
- * was DNS. Similar, if nothing specific was
- * requested. However, if only resolving via LLMNR was
- * requested we return that. */
-
- if (flags & SD_RESOLVED_DNS)
- return DNS_PROTOCOL_DNS;
- if (flags & SD_RESOLVED_LLMNR)
- return DNS_PROTOCOL_LLMNR;
-
- return DNS_PROTOCOL_DNS;
-}
-
-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), DNS_ANSWER_AUTHENTICATED);
- 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), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
- _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, flags);
-}
-
-static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
- int r;
-
- assert(q);
- assert(key);
- assert(answer);
-
- if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- 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, DNS_ANSWER_AUTHENTICATED);
- 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, DNS_ANSWER_AUTHENTICATED);
- 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), DNS_ANSWER_AUTHENTICATED);
- 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), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- 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;
assert(q);
assert(state);
- /* Tries to synthesize localhost RR replies where appropriate */
+ /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
+ * the normal lookup finished. The data from the network hence takes precedence over the data we
+ * synthesize. (But note that many scopes refuse to resolve certain domain names) */
if (!IN_SET(*state,
DNS_TRANSACTION_RCODE_FAILURE,
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
- DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
+ DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND))
return 0;
- for (i = 0; i < q->question->n_keys; i++) {
- 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)
- continue;
-
- name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
-
- if (is_localhost(name)) {
+ r = dns_synthesize_answer(
+ q->manager,
+ q->question_utf8,
+ q->ifindex,
+ &answer);
- r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
+ if (r <= 0)
+ return r;
- } else if (manager_is_own_hostname(q->manager, name)) {
+ dns_query_reset_answer(q);
- 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");
-
- } else if (is_gateway_hostname(name)) {
+ q->answer = answer;
+ answer = NULL;
+ q->answer_rcode = DNS_RCODE_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
- r = synthesize_gateway_rr(q, q->question->keys[i], &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
+ *state = DNS_TRANSACTION_SUCCESS;
- } 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) {
+ return 1;
+}
- 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");
+static int dns_query_try_etc_hosts(DnsQuery *q) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ int r;
- } else if (dns_name_address(name, &af, &address) > 0) {
+ assert(q);
- 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");
+ /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The
+ * data from /etc/hosts hence takes precedence over the network. */
- r = synthesize_gateway_ptr(q, af, &address, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
- }
- }
+ r = manager_etc_hosts_lookup(
+ q->manager,
+ q->question_utf8,
+ &answer);
+ if (r <= 0)
+ return r;
- if (!answer)
- return 0;
+ dns_query_reset_answer(q);
- dns_answer_unref(q->answer);
q->answer = answer;
answer = NULL;
-
q->answer_rcode = DNS_RCODE_SUCCESS;
- q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
- q->answer_family = SYNTHESIZE_FAMILY(q->flags);
-
- *state = DNS_TRANSACTION_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
return 1;
}
@@ -884,7 +628,6 @@ int dns_query_go(DnsQuery *q) {
DnsScopeMatch found = DNS_SCOPE_NO;
DnsScope *s, *first = NULL;
DnsQueryCandidate *c;
- const char *name;
int r;
assert(q);
@@ -892,13 +635,21 @@ int dns_query_go(DnsQuery *q) {
if (q->state != DNS_TRANSACTION_NULL)
return 0;
- assert(q->question);
- assert(q->question->n_keys > 0);
-
- name = dns_question_first_name(q->question);
+ r = dns_query_try_etc_hosts(q);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
+ return 1;
+ }
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
+ const char *name;
+
+ name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
+ if (!name)
+ continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
@@ -923,7 +674,10 @@ int dns_query_go(DnsQuery *q) {
if (found == DNS_SCOPE_NO) {
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ return r;
+
dns_query_complete(q, state);
return 1;
}
@@ -934,6 +688,11 @@ int dns_query_go(DnsQuery *q) {
LIST_FOREACH(scopes, s, first->scopes_next) {
DnsScopeMatch match;
+ const char *name;
+
+ name = dns_question_first_name(dns_query_question_for_protocol(q, s->protocol));
+ if (!name)
+ continue;
match = dns_scope_good_domain(s, q->ifindex, q->flags, name);
if (match < 0)
@@ -947,10 +706,7 @@ int dns_query_go(DnsQuery *q) {
goto fail;
}
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = 0;
- q->answer_family = AF_UNSPEC;
- q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ dns_query_reset_answer(q);
r = sd_event_add_time(
q->manager->event,
@@ -996,11 +752,23 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
assert(q);
if (!c) {
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
return;
}
+ if (c->error_code != 0) {
+ /* If the candidate had an error condition of its own, start with that. */
+ state = DNS_TRANSACTION_ERRNO;
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = 0;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = c->error_code;
+ }
+
SET_FOREACH(t, c->transactions, i) {
switch (t->state) {
@@ -1008,12 +776,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
case DNS_TRANSACTION_SUCCESS: {
/* We found a successfuly reply, merge it into the answer */
r = dns_answer_extend(&q->answer, t->answer);
- if (r < 0) {
- dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
q->answer_rcode = t->answer_rcode;
+ q->answer_errno = 0;
if (t->answer_authenticated) {
has_authenticated = true;
@@ -1044,6 +811,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_errno = t->answer_errno;
state = t->state;
break;
@@ -1061,8 +829,16 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
dns_search_domain_unref(q->answer_search_domain);
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
+ return;
+
+fail:
+ q->answer_errno = -r;
+ dns_query_complete(q, DNS_TRANSACTION_ERRNO);
}
void dns_query_ready(DnsQuery *q) {
@@ -1115,33 +891,60 @@ void dns_query_ready(DnsQuery *q) {
}
static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
- _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
- int r;
+ _cleanup_(dns_question_unrefp) DnsQuestion *nq_idna = NULL, *nq_utf8 = NULL;
+ int r, k;
assert(q);
- log_debug("Following CNAME %s → %s", dns_question_first_name(q->question), cname->cname.name);
-
q->n_cname_redirects ++;
if (q->n_cname_redirects > CNAME_MAX)
return -ELOOP;
- r = dns_question_cname_redirect(q->question, cname, &nq);
+ r = dns_question_cname_redirect(q->question_idna, cname, &nq_idna);
if (r < 0)
return r;
+ else if (r > 0)
+ log_debug("Following CNAME/DNAME %s → %s.", dns_question_first_name(q->question_idna), dns_question_first_name(nq_idna));
- dns_question_unref(q->question);
- q->question = nq;
- nq = NULL;
+ k = dns_question_is_equal(q->question_idna, q->question_utf8);
+ if (k < 0)
+ return r;
+ if (k > 0) {
+ /* Same question? Shortcut new question generation */
+ nq_utf8 = dns_question_ref(nq_idna);
+ k = r;
+ } else {
+ k = dns_question_cname_redirect(q->question_utf8, cname, &nq_utf8);
+ if (k < 0)
+ return k;
+ else if (k > 0)
+ log_debug("Following UTF8 CNAME/DNAME %s → %s.", dns_question_first_name(q->question_utf8), dns_question_first_name(nq_utf8));
+ }
- dns_query_stop(q);
+ if (r == 0 && k == 0) /* No actual cname happened? */
+ return -ELOOP;
+
+ dns_question_unref(q->question_idna);
+ q->question_idna = nq_idna;
+ nq_idna = NULL;
+
+ dns_question_unref(q->question_utf8);
+ q->question_utf8 = nq_utf8;
+ nq_utf8 = NULL;
+
+ dns_query_free_candidates(q);
+ dns_query_reset_answer(q);
q->state = DNS_TRANSACTION_NULL;
+ /* Turn off searching for the new name */
+ q->flags |= SD_RESOLVED_NO_SEARCH;
+
return 0;
}
int dns_query_process_cname(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
+ DnsQuestion *question;
DnsResourceRecord *rr;
int r;
@@ -1150,15 +953,16 @@ int dns_query_process_cname(DnsQuery *q) {
if (!IN_SET(q->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_NULL))
return DNS_QUERY_NOMATCH;
- DNS_ANSWER_FOREACH(rr, q->answer) {
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
if (r < 0)
return r;
if (r > 0)
return DNS_QUERY_MATCH; /* The answer matches directly, no need to follow cnames */
- r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ r = dns_question_matches_cname_or_dname(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
if (r < 0)
return r;
if (r > 0 && !cname)
@@ -1219,3 +1023,42 @@ int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) {
return 0;
}
+
+DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol) {
+ assert(q);
+
+ switch (protocol) {
+
+ case DNS_PROTOCOL_DNS:
+ return q->question_idna;
+
+ case DNS_PROTOCOL_MDNS:
+ case DNS_PROTOCOL_LLMNR:
+ return q->question_utf8;
+
+ default:
+ return NULL;
+ }
+}
+
+const char *dns_query_string(DnsQuery *q) {
+ const char *name;
+ int r;
+
+ /* Returns a somewhat useful human-readable lookup key string for this query */
+
+ if (q->request_address_string)
+ return q->request_address_string;
+
+ if (q->request_address_valid) {
+ r = in_addr_to_string(q->request_family, &q->request_address, &q->request_address_string);
+ if (r >= 0)
+ return q->request_address_string;
+ }
+
+ name = dns_question_first_name(q->question_utf8);
+ if (name)
+ return name;
+
+ return dns_question_first_name(q->question_idna);
+}
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 4a0d265a2d..c7e4ce9a00 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -59,7 +59,13 @@ struct DnsQuery {
unsigned n_auxiliary_queries;
int auxiliary_result;
- DnsQuestion *question;
+ /* The question, formatted in IDNA for use on classic DNS, and as UTF8 for use in LLMNR or mDNS. Note that even
+ * on classic DNS some labels might use UTF8 encoding. Specifically, DNS-SD service names (in contrast to their
+ * domain suffixes) use UTF-8 encoding even on DNS. Thus, the difference between these two fields is mostly
+ * relevant only for explicit *hostname* lookups as well as the domain suffixes of service lookups. */
+ DnsQuestion *question_idna;
+ DnsQuestion *question_utf8;
+
uint64_t flags;
int ifindex;
@@ -77,6 +83,7 @@ struct DnsQuery {
DnsProtocol answer_protocol;
int answer_family;
DnsSearchDomain *answer_search_domain;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Bus client information */
sd_bus_message *request;
@@ -84,6 +91,7 @@ struct DnsQuery {
bool request_address_valid;
union in_addr_union request_address;
unsigned block_all_complete;
+ char *request_address_string;
/* Completion callback */
void (*complete)(DnsQuery* q);
@@ -104,7 +112,7 @@ enum {
DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
void dns_query_candidate_notify(DnsQueryCandidate *c);
-int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
+int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);
DnsQuery *dns_query_free(DnsQuery *q);
int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
@@ -116,4 +124,8 @@ int dns_query_process_cname(DnsQuery *q);
int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
+DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol);
+
+const char *dns_query_string(DnsQuery *q);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 4ed7434d3c..1e41a9aa3c 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
+#include "dns-type.h"
#include "resolved-dns-question.h"
DnsQuestion *dns_question_new(unsigned n) {
@@ -107,7 +108,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *s
return 0;
}
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
+int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
unsigned i;
int r;
@@ -116,7 +117,14 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char
if (!q)
return 0;
+ if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
+ return 0;
+
for (i = 0; i < q->n_keys; i++) {
+ /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
+ if (!dns_type_may_redirect(q->keys[i]->type))
+ return 0;
+
r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
if (r != 0)
return r;
@@ -144,18 +152,23 @@ int dns_question_is_valid_for_query(DnsQuestion *q) {
return 0;
/* Check that all keys in this question bear the same name */
- for (i = 1; i < q->n_keys; i++) {
+ for (i = 0; i < q->n_keys; i++) {
assert(q->keys[i]);
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
- if (r <= 0)
- return r;
+ if (i > 0) {
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
+ if (r <= 0)
+ return r;
+ }
+
+ if (!dns_type_is_valid_query(q->keys[i]->type))
+ return 0;
}
return 1;
}
-int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
+int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
unsigned j;
int r;
@@ -177,6 +190,9 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
unsigned j;
int r;
+ if (a == b)
+ return 1;
+
if (!a)
return !b || b->n_keys == 0;
if (!b)
@@ -201,32 +217,27 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
_cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
+ DnsResourceKey *key;
bool same = true;
- unsigned i;
int r;
assert(cname);
assert(ret);
assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
- if (!q) {
- n = dns_question_new(0);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- n = 0;
+ if (dns_question_size(q) <= 0) {
+ *ret = NULL;
return 0;
}
- for (i = 0; i < q->n_keys; i++) {
+ DNS_QUESTION_FOREACH(key, q) {
_cleanup_free_ char *destination = NULL;
const char *d;
if (cname->key->type == DNS_TYPE_CNAME)
d = cname->cname.name;
else {
- r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
+ r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
if (r < 0)
return r;
if (r == 0)
@@ -235,7 +246,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
d = destination;
}
- r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d);
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d);
if (r < 0)
return r;
@@ -245,9 +256,9 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
}
}
+ /* Fully the same, indicate we didn't do a thing */
if (same) {
- /* Shortcut, the names are already right */
- *ret = dns_question_ref(q);
+ *ret = NULL;
return 0;
}
@@ -256,10 +267,10 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
return -ENOMEM;
/* Create a new question, and patch in the new name */
- for (i = 0; i < q->n_keys; i++) {
+ DNS_QUESTION_FOREACH(key, q) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
- k = dns_resource_key_new_redirect(q->keys[i], cname);
+ k = dns_resource_key_new_redirect(key, cname);
if (!k)
return -ENOMEM;
@@ -285,8 +296,9 @@ const char *dns_question_first_name(DnsQuestion *q) {
return DNS_RESOURCE_KEY_NAME(q->keys[0]);
}
-int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
_cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *buf = NULL;
int r;
assert(ret);
@@ -295,6 +307,14 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return -EAFNOSUPPORT;
+ if (convert_idna) {
+ r = dns_name_apply_idna(name, &buf);
+ if (r < 0)
+ return r;
+
+ name = buf;
+ }
+
q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
if (!q)
return -ENOMEM;
@@ -365,13 +385,60 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_
return 0;
}
-int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) {
+int dns_question_new_service(
+ DnsQuestion **ret,
+ const char *service,
+ const char *type,
+ const char *domain,
+ bool with_txt,
+ bool convert_idna) {
+
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
_cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *buf = NULL, *joined = NULL;
+ const char *name;
int r;
assert(ret);
- assert(name);
+
+ /* We support three modes of invocation:
+ *
+ * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
+ * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
+ * that's necessary.
+ *
+ * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
+ * style prefix. In this case we'll IDNA convert the domain, if that's requested.
+ *
+ * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
+ * together. The service name is never IDNA converted, and the domain is if requested.
+ *
+ * It's not supported to specify a service name without a type, or no domain name.
+ */
+
+ if (!domain)
+ return -EINVAL;
+
+ if (type) {
+ if (convert_idna) {
+ r = dns_name_apply_idna(domain, &buf);
+ if (r < 0)
+ return r;
+
+ domain = buf;
+ }
+
+ r = dns_service_join(service, type, domain, &joined);
+ if (r < 0)
+ return r;
+
+ name = joined;
+ } else {
+ if (service)
+ return -EINVAL;
+
+ name = domain;
+ }
q = dns_question_new(1 + with_txt);
if (!q)
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 5ffb63e250..98e1f0e366 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -38,22 +38,26 @@ DnsQuestion *dns_question_new(unsigned n);
DnsQuestion *dns_question_ref(DnsQuestion *q);
DnsQuestion *dns_question_unref(DnsQuestion *q);
-int dns_question_new_address(DnsQuestion **ret, int family, const char *name);
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna);
int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
-int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt);
+int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain);
-int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
+int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain);
int dns_question_is_valid_for_query(DnsQuestion *q);
-int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
+int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k);
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
const char *dns_question_first_name(DnsQuestion *q);
+static inline unsigned dns_question_size(DnsQuestion *q) {
+ return q ? q->n_keys : 0;
+}
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);
#define _DNS_QUESTION_FOREACH(u, key, q) \
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index dbf840157f..7273ef3825 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -334,6 +334,46 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
return 0;
}
+bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) {
+ assert(a);
+ assert(b);
+
+ /* Try to replace one RR key by another if they are identical, thus saving a bit of memory. Note that we do
+ * this only for RR keys, not for RRs themselves, as they carry a lot of additional metadata (where they come
+ * from, validity data, and suchlike), and cannot be replaced so easily by other RRs that have the same
+ * superficial data. */
+
+ if (!*a)
+ return false;
+ if (!*b)
+ return false;
+
+ /* We refuse merging const keys */
+ if ((*a)->n_ref == (unsigned) -1)
+ return false;
+ if ((*b)->n_ref == (unsigned) -1)
+ return false;
+
+ /* Already the same? */
+ if (*a == *b)
+ return true;
+
+ /* Are they really identical? */
+ if (dns_resource_key_equal(*a, *b) <= 0)
+ return false;
+
+ /* Keep the one which already has more references. */
+ if ((*a)->n_ref > (*b)->n_ref) {
+ dns_resource_key_unref(*b);
+ *b = dns_resource_key_ref(*a);
+ } else {
+ dns_resource_key_unref(*a);
+ *a = dns_resource_key_ref(*b);
+ }
+
+ return true;
+}
+
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
DnsResourceRecord *rr;
@@ -344,6 +384,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
rr->n_ref = 1;
rr->key = dns_resource_key_ref(key);
rr->expiry = USEC_INFINITY;
+ rr->n_skip_labels_signer = rr->n_skip_labels_source = (unsigned) -1;
return rr;
}
@@ -1085,6 +1126,88 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
return 0;
}
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) {
+ const char *n;
+ int r;
+
+ assert(rr);
+ assert(ret);
+
+ /* Returns the RRset's signer, if it is known. */
+
+ if (rr->n_skip_labels_signer == (unsigned) -1)
+ return -ENODATA;
+
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_skip(n, rr->n_skip_labels_signer, &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ *ret = n;
+ return 0;
+}
+
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) {
+ const char *n;
+ int r;
+
+ assert(rr);
+ assert(ret);
+
+ /* Returns the RRset's synthesizing source, if it is known. */
+
+ if (rr->n_skip_labels_source == (unsigned) -1)
+ return -ENODATA;
+
+ n = DNS_RESOURCE_KEY_NAME(rr->key);
+ r = dns_name_skip(n, rr->n_skip_labels_source, &n);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ *ret = n;
+ return 0;
+}
+
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) {
+ const char *signer;
+ int r;
+
+ assert(rr);
+
+ r = dns_resource_record_signer(rr, &signer);
+ if (r < 0)
+ return r;
+
+ return dns_name_equal(zone, signer);
+}
+
+int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
+ int r;
+
+ assert(rr);
+
+ /* Returns > 0 if the RR is generated from a wildcard, and is not the asterisk name itself */
+
+ if (rr->n_skip_labels_source == (unsigned) -1)
+ return -ENODATA;
+
+ if (rr->n_skip_labels_source == 0)
+ return 0;
+
+ if (rr->n_skip_labels_source > 1)
+ return 1;
+
+ r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(rr->key), "*");
+ if (r < 0)
+ return r;
+
+ return !r;
+}
+
static void dns_resource_record_hash_func(const void *i, struct siphash *state) {
const DnsResourceRecord *rr = i;
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index fe29a41566..d9c31e81c5 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -81,7 +81,7 @@ enum {
};
struct DnsResourceKey {
- unsigned n_ref;
+ unsigned n_ref; /* (unsigned -1) for const keys, see below */
uint16_t class, type;
char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
};
@@ -108,14 +108,24 @@ struct DnsTxtItem {
struct DnsResourceRecord {
unsigned n_ref;
DnsResourceKey *key;
+
char *to_string;
+
uint32_t ttl;
usec_t expiry; /* RRSIG signature expiry */
+
+ /* How many labels to strip to determine "signer" of the RRSIG (aka, the zone). -1 if not signed. */
+ unsigned n_skip_labels_signer;
+ /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
+ unsigned n_skip_labels_source;
+
bool unparseable:1;
+
bool wire_format_canonical:1;
void *wire_format;
size_t wire_format_size;
size_t wire_format_rdata_offset;
+
union {
struct {
void *data;
@@ -284,6 +294,8 @@ static inline bool dns_key_is_shared(const DnsResourceKey *key) {
return IN_SET(key->type, DNS_TYPE_PTR);
}
+bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b);
+
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key);
DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name);
DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr);
@@ -296,6 +308,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone);
+int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
+
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index dd3609bd12..ac4887abea 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -67,11 +67,9 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
* changes. */
if (l)
- s->dnssec_mode = l->dnssec_mode;
- if (s->dnssec_mode == _DNSSEC_MODE_INVALID)
- s->dnssec_mode = m->dnssec_mode;
- if (s->dnssec_mode == _DNSSEC_MODE_INVALID)
- s->dnssec_mode = DNSSEC_NO;
+ s->dnssec_mode = link_get_dnssec_mode(l);
+ else
+ s->dnssec_mode = manager_get_dnssec_mode(m);
}
LIST_PREPEND(scopes, m->dns_scopes, s);
@@ -1017,3 +1015,22 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
return dns_name_is_single_label(name);
}
+
+bool dns_scope_network_good(DnsScope *s) {
+ Iterator i;
+ Link *l;
+
+ /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
+ * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
+ * DNS scope we check whether there are any links that are up and have an address. */
+
+ if (s->link)
+ return true;
+
+ HASHMAP_FOREACH(l, s->manager->links, i) {
+ if (link_relevant(l, AF_UNSPEC, false))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index a0676bd30e..f9b63d56d9 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -107,3 +107,5 @@ void dns_scope_dump(DnsScope *s, FILE *f);
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
+
+bool dns_scope_network_good(DnsScope *s);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
index f9d966abb1..356c05b9a4 100644
--- a/src/resolve/resolved-dns-search-domain.c
+++ b/src/resolve/resolved-dns-search-domain.c
@@ -42,9 +42,6 @@ int dns_search_domain_new(
if (r < 0)
return r;
- if (dns_name_is_root(normalized))
- return -EINVAL;
-
if (l) {
if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
return -E2BIG;
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
index 2e0af31dda..c1903b334f 100644
--- a/src/resolve/resolved-dns-search-domain.h
+++ b/src/resolve/resolved-dns-search-domain.h
@@ -44,6 +44,7 @@ struct DnsSearchDomain {
char *name;
bool marked:1;
+ bool route_only:1;
bool linked:1;
LIST_FIELDS(DnsSearchDomain, domains);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 0969e31e8a..e1d2025863 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sd-messages.h>
+
#include "alloc-util.h"
#include "resolved-dns-server.h"
#include "resolved-resolv-conf.h"
@@ -232,7 +234,9 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
return;
if (s->verified_feature_level != level) {
- log_debug("Verified feature level %s.", dns_server_feature_level_to_string(level));
+ log_debug("Verified we get a response at feature level %s from DNS server %s.",
+ dns_server_feature_level_to_string(level),
+ dns_server_string(s));
s->verified_feature_level = level;
}
@@ -246,13 +250,18 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve
if (s->possible_feature_level == level)
s->n_failed_udp = 0;
+ /* If the RRSIG data is missing, then we can only validate EDNS0 at max */
+ if (s->packet_rrsig_missing && level >= DNS_SERVER_FEATURE_LEVEL_DO)
+ level = DNS_SERVER_FEATURE_LEVEL_DO - 1;
+
+ /* If the OPT RR got lost, then we can only validate UDP at max */
+ if (s->packet_bad_opt && level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ level = DNS_SERVER_FEATURE_LEVEL_EDNS0 - 1;
+
+ /* Even if we successfully receive a reply to a request announcing support for large packets,
+ that does not mean we can necessarily receive large packets. */
if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
- /* Even if we successfully receive a reply to a request announcing support for large packets,
- that does not mean we can necessarily receive large packets. */
- dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_LARGE - 1);
- else
- /* A successful UDP reply, verifies UDP, ENDS0 and DO levels */
- dns_server_verified(s, level);
+ level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
} else if (protocol == IPPROTO_TCP) {
@@ -260,9 +269,11 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve
s->n_failed_tcp = 0;
/* Successful TCP connections are only useful to verify the TCP feature level. */
- dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_TCP);
+ level = DNS_SERVER_FEATURE_LEVEL_TCP;
}
+ dns_server_verified(s, level);
+
/* Remember the size of the largest UDP packet we received from a server,
we know that we can always announce support for packets with at least
this size. */
@@ -294,7 +305,6 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
- assert(s->manager);
/* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */
@@ -306,7 +316,6 @@ void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
- assert(s->manager);
/* Invoked whenever we get a packet with TC bit set. */
@@ -316,13 +325,30 @@ void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
s->packet_truncated = true;
}
-void dns_server_packet_rrsig_missing(DnsServer *s) {
+void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
- assert(s->manager);
- log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", dns_server_string(s));
+ if (level < DNS_SERVER_FEATURE_LEVEL_DO)
+ return;
+
+ /* If the RRSIG RRs are missing, we have to downgrade what we previously verified */
+ if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO)
+ s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_DO-1;
- s->rrsig_missing = true;
+ s->packet_rrsig_missing = true;
+}
+
+void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
+
+ if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ return;
+
+ /* If the OPT RR got lost, we have to downgrade what we previously verified */
+ if (s->verified_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
+ s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0-1;
+
+ s->packet_bad_opt = true;
}
static bool dns_server_grace_period_expired(DnsServer *s) {
@@ -352,6 +378,17 @@ static void dns_server_reset_counters(DnsServer *s) {
s->packet_failed = false;
s->packet_truncated = false;
s->verified_usec = 0;
+
+ /* Note that we do not reset s->packet_bad_opt and s->packet_rrsig_missing here. We reset them only when the
+ * grace period ends, but not when lowering the possible feature level, as a lower level feature level should
+ * not make RRSIGs appear or OPT appear, but rather make them disappear. If the reappear anyway, then that's
+ * indication for a differently broken OPT/RRSIG implementation, and we really don't want to support that
+ * either.
+ *
+ * This is particularly important to deal with certain Belkin routers which break OPT for certain lookups (A),
+ * but pass traffic through for others (AAAA). If we detect the broken behaviour on one lookup we should not
+ * reenable it for another, because we cannot validate things anyway, given that the RRSIG/OPT data will be
+ * incomplete. */
}
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
@@ -361,11 +398,13 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
dns_server_grace_period_expired(s)) {
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
- s->rrsig_missing = false;
dns_server_reset_counters(s);
- log_info("Grace period over, resuming full feature set (%s) for DNS server %s",
+ s->packet_bad_opt = false;
+ s->packet_rrsig_missing = false;
+
+ log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
dns_server_string(s));
@@ -375,46 +414,75 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
DnsServerFeatureLevel p = s->possible_feature_level;
if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP)
+ s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP) {
/* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't
* work. Upgrade back to UDP again. */
+ log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again...");
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
+
+ } else if (s->packet_bad_opt &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
+
+ /* A reply to one of our EDNS0 queries didn't carry a valid OPT RR, then downgrade to below
+ * EDNS0 levels. After all, some records generate different responses with and without OPT RR
+ * in the request. Example:
+ * https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
+
+ log_debug("Server doesn't support EDNS(0) properly, downgrading feature level...");
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
- else if ((s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) ||
- (s->packet_failed &&
- s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) ||
- (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
- s->packet_truncated &&
- s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP))
-
- /* Downgrade the feature one level, maybe things will work better then. We do this under any of
- * three conditions:
- *
- * 1. We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If
- * the packets are lost, maybe the server cannot parse them, hence downgrading sounds like a
- * good idea. We might downgrade all the way down to TCP this way.
- *
- * 2. We got a failure packet, and are at a feature level above UDP. Note that in this case we
- * downgrade no further than UDP, under the assumption that a failure packet indicates an
- * incompatible packet contents, but not a problem with the transport.
- *
- * 3. We got too many TCP connection failures in a row, we had at least one truncated packet,
- * and are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or
- * EDNS0 data we hope to make the packet smaller, so that it still works via UDP given that
- * TCP appears not to be a fallback. Note that if we are already at the lowest UDP level, we
- * don't go further down, since that's TCP, and TCP failed too often after all.
- */
+ } else if (s->packet_rrsig_missing &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_DO) {
+ /* RRSIG data was missing on a EDNS0 packet with DO bit set. This means the server doesn't
+ * augment responses with DNSSEC RRs. If so, let's better not ask the server for it anymore,
+ * after all some servers generate different replies depending if an OPT RR is in the query or
+ * not. */
+
+ log_debug("Detected server responses lack RRSIG records, downgrading feature level...");
+ s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0;
+
+ } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the
+ * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good
+ * idea. We might downgrade all the way down to TCP this way. */
+
+ log_debug("Lost too many UDP packets, downgrading feature level...");
+ s->possible_feature_level--;
+
+ } else if (s->packet_failed &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We got a failure packet, and are at a feature level above UDP. Note that in this case we
+ * downgrade no further than UDP, under the assumption that a failure packet indicates an
+ * incompatible packet contents, but not a problem with the transport. */
+
+ log_debug("Got server failure, downgrading feature level...");
+ s->possible_feature_level--;
+
+ } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->packet_truncated &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) {
+
+ /* We got too many TCP connection failures in a row, we had at least one truncated packet, and
+ * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0
+ * data we hope to make the packet smaller, so that it still works via UDP given that TCP
+ * appears not to be a fallback. Note that if we are already at the lowest UDP level, we don't
+ * go further down, since that's TCP, and TCP failed too often after all. */
+
+ log_debug("Got too many failed TCP connection failures and truncated UDP packets, downgrading feature level...");
s->possible_feature_level--;
+ }
if (p != s->possible_feature_level) {
/* We changed the feature level, reset the counting */
dns_server_reset_counters(s);
- log_warning("Using degraded feature set (%s) for DNS server %s",
+ log_warning("Using degraded feature set (%s) for DNS server %s.",
dns_server_feature_level_to_string(s->possible_feature_level),
dns_server_string(s));
}
@@ -468,7 +536,10 @@ bool dns_server_dnssec_supported(DnsServer *server) {
if (server->possible_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
return false;
- if (server->rrsig_missing)
+ if (server->packet_bad_opt)
+ return false;
+
+ if (server->packet_rrsig_missing)
return false;
/* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */
@@ -478,6 +549,22 @@ bool dns_server_dnssec_supported(DnsServer *server) {
return true;
}
+void dns_server_warn_downgrade(DnsServer *server) {
+ assert(server);
+
+ if (server->warned_downgrade)
+ return;
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE),
+ LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
+ "DNS_SERVER=%s", dns_server_string(server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level),
+ NULL);
+
+ server->warned_downgrade = true;
+}
+
static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 323f702903..2a3c921678 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -68,19 +68,22 @@ struct DnsServer {
DnsServerFeatureLevel verified_feature_level;
DnsServerFeatureLevel possible_feature_level;
+
size_t received_udp_packet_max;
+
unsigned n_failed_udp;
unsigned n_failed_tcp;
+
bool packet_failed:1;
bool packet_truncated:1;
+ bool packet_bad_opt:1;
+ bool packet_rrsig_missing:1;
+
usec_t verified_usec;
usec_t features_grace_period_usec;
- /* Indicates whether responses are augmented with RRSIG by
- * server or not. Note that this is orthogonal to the feature
- * level stuff, as it's only information describing responses,
- * and has no effect on how the questions are asked. */
- bool rrsig_missing:1;
+ /* Whether we already warned about downgrading to non-DNSSEC mode for this server */
+ bool warned_downgrade:1;
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked:1;
@@ -108,7 +111,8 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve
void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec);
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level);
void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level);
-void dns_server_packet_rrsig_missing(DnsServer *s);
+void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level);
+void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level);
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
@@ -118,6 +122,8 @@ const char *dns_server_string(DnsServer *server);
bool dns_server_dnssec_supported(DnsServer *server);
+void dns_server_warn_downgrade(DnsServer *server);
+
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
void dns_server_unlink_all(DnsServer *first);
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
new file mode 100644
index 0000000000..f4a43dee8c
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.c
@@ -0,0 +1,413 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "hostname-util.h"
+#include "local-addresses.h"
+#include "resolved-dns-synthesize.h"
+
+int dns_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 and we
+ * only return localhost RRs, we synthesize the answer for
+ * localhost. */
+
+ if (ifindex > 0)
+ return ifindex;
+
+ return LOOPBACK_IFINDEX;
+}
+
+int dns_synthesize_family(uint64_t flags) {
+
+ /* Picks an address family depending on set flags. This is
+ * purely for synthesized answers, where the family we return
+ * for the reply should match what was requested in the
+ * question, even though we are synthesizing the answer
+ * here. */
+
+ if (!(flags & SD_RESOLVED_DNS)) {
+ if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
+ return AF_INET;
+ if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
+ return AF_INET6;
+ }
+
+ return AF_UNSPEC;
+}
+
+DnsProtocol dns_synthesize_protocol(uint64_t flags) {
+
+ /* Similar as dns_synthesize_family() but does this for the
+ * protocol. If resolving via DNS was requested, we claim it
+ * was DNS. Similar, if nothing specific was
+ * requested. However, if only resolving via LLMNR was
+ * requested we return that. */
+
+ if (flags & SD_RESOLVED_DNS)
+ return DNS_PROTOCOL_DNS;
+ if (flags & SD_RESOLVED_LLMNR)
+ return DNS_PROTOCOL_LLMNR;
+ if (flags & SD_RESOLVED_MDNS)
+ return DNS_PROTOCOL_MDNS;
+
+ return DNS_PROTOCOL_DNS;
+}
+
+static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ 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, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ 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, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
+ _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, flags);
+}
+
+static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ 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, DNS_ANSWER_AUTHENTICATED);
+ 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, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_addresses(m->rtnl, 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 = dns_synthesize_ifindex(ifindex),
+ .address.in.s_addr = htobe32(0x7F000002),
+ };
+
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET6,
+ .ifindex = dns_synthesize_ifindex(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(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n, r;
+
+ assert(m);
+ 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", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ n = local_addresses(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
+ if (r < 0)
+ return r;
+
+ return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
+}
+
+static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_gateways(m->rtnl, 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(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ n = local_gateways(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
+}
+
+int dns_synthesize_answer(
+ Manager *m,
+ DnsQuestion *q,
+ int ifindex,
+ DnsAnswer **ret) {
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnsResourceKey *key;
+ bool found = false;
+ int r;
+
+ assert(m);
+ assert(q);
+
+ DNS_QUESTION_FOREACH(key, q) {
+ union in_addr_union address;
+ const char *name;
+ int af;
+
+ if (key->class != DNS_CLASS_IN &&
+ key->class != DNS_CLASS_ANY)
+ continue;
+
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ if (is_localhost(name)) {
+
+ r = synthesize_localhost_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
+
+ } else if (manager_is_own_hostname(m, name)) {
+
+ r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
+
+ } else if (is_gateway_hostname(name)) {
+
+ r = synthesize_gateway_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
+
+ } 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) {
+
+ r = synthesize_localhost_ptr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
+
+ } else if (dns_name_address(name, &af, &address) > 0) {
+
+ r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
+
+ r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
+ } else
+ continue;
+
+ found = true;
+ }
+
+ r = found;
+
+ if (ret) {
+ *ret = answer;
+ answer = NULL;
+ }
+
+ return r;
+}
diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h
new file mode 100644
index 0000000000..5d829bb2e7
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.h
@@ -0,0 +1,30 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-dns-answer.h"
+#include "resolved-dns-question.h"
+#include "resolved-manager.h"
+
+int dns_synthesize_ifindex(int ifindex);
+int dns_synthesize_family(uint64_t flags);
+DnsProtocol dns_synthesize_protocol(uint64_t flags);
+
+int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 9ee10f21c8..802ad860a4 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -24,6 +24,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dns-domain.h"
+#include "errno-list.h"
#include "fd-util.h"
#include "random-util.h"
#include "resolved-dns-cache.h"
@@ -31,6 +32,8 @@
#include "resolved-llmnr.h"
#include "string-table.h"
+#define TRANSACTIONS_MAX 4096
+
static void dns_transaction_reset_answer(DnsTransaction *t) {
assert(t);
@@ -41,6 +44,18 @@ static void dns_transaction_reset_answer(DnsTransaction *t) {
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
t->answer_authenticated = false;
t->answer_nsec_ttl = (uint32_t) -1;
+ t->answer_errno = 0;
+}
+
+static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
+ DnsTransaction *z;
+
+ assert(t);
+
+ while ((z = set_steal_first(t->dnssec_transactions))) {
+ set_remove(z->notify_transactions, t);
+ dns_transaction_gc(z);
+ }
}
static void dns_transaction_close_connection(DnsTransaction *t) {
@@ -95,10 +110,7 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
set_remove(z->dnssec_transactions, t);
set_free(t->notify_transactions);
- while ((z = set_steal_first(t->dnssec_transactions))) {
- set_remove(z->notify_transactions, t);
- dns_transaction_gc(z);
- }
+ dns_transaction_flush_dnssec_transactions(t);
set_free(t->dnssec_transactions);
dns_answer_unref(t->validated_keys);
@@ -127,6 +139,22 @@ bool dns_transaction_gc(DnsTransaction *t) {
return true;
}
+static uint16_t pick_new_id(Manager *m) {
+ uint16_t new_id;
+
+ /* Find a fresh, unused transaction id. Note that this loop is bounded because there's a limit on the number of
+ * transactions, and it's much lower than the space of IDs. */
+
+ assert_cc(TRANSACTIONS_MAX < 0xFFFF);
+
+ do
+ random_bytes(&new_id, sizeof(new_id));
+ while (new_id == 0 ||
+ hashmap_get(m->dns_transactions, UINT_TO_PTR(new_id)));
+
+ return new_id;
+}
+
int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
_cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
int r;
@@ -145,6 +173,9 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY)
return -EOPNOTSUPP;
+ if (hashmap_size(s->manager->dns_transactions) >= TRANSACTIONS_MAX)
+ return -EBUSY;
+
r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
if (r < 0)
return r;
@@ -164,11 +195,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
t->key = dns_resource_key_ref(key);
t->current_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
- /* Find a fresh, unused transaction id */
- do
- random_bytes(&t->id, sizeof(t->id));
- while (t->id == 0 ||
- hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
+ t->id = pick_new_id(s->manager);
r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
if (r < 0) {
@@ -195,6 +222,22 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
return 0;
}
+static void dns_transaction_shuffle_id(DnsTransaction *t) {
+ uint16_t new_id;
+ assert(t);
+
+ /* Pick a new ID for this transaction. */
+
+ new_id = pick_new_id(t->scope->manager);
+ assert_se(hashmap_remove_and_put(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id), UINT_TO_PTR(new_id), t) >= 0);
+
+ log_debug("Transaction %" PRIu16 " is now %" PRIu16 ".", t->id, new_id);
+ t->id = new_id;
+
+ /* Make sure we generate a new packet with the new ID */
+ t->sent = dns_packet_unref(t->sent);
+}
+
static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
_cleanup_free_ char *pretty = NULL;
DnsZoneItem *z;
@@ -244,6 +287,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
DnsZoneItem *z;
DnsTransaction *d;
Iterator i;
+ const char *st;
assert(t);
assert(!DNS_TRANSACTION_IS_LIVE(state));
@@ -255,19 +299,26 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", dns_transaction_key_string(t),
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
+ "DNS_SERVER=%s", dns_server_string(t->server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level),
NULL);
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
+ if (state == DNS_TRANSACTION_ERRNO)
+ st = errno_to_name(t->answer_errno);
+ else
+ st = dns_transaction_state_to_string(state);
+
log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
t->id,
dns_transaction_key_string(t),
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
- dns_transaction_state_to_string(state),
+ st,
t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
t->answer_authenticated ? "authenticated" : "unsigned");
@@ -350,8 +401,30 @@ static void dns_transaction_retry(DnsTransaction *t) {
dns_scope_next_dns_server(t->scope);
r = dns_transaction_go(t);
- if (r < 0)
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r < 0) {
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ }
+}
+
+static int dns_transaction_maybe_restart(DnsTransaction *t) {
+ assert(t);
+
+ if (!t->server)
+ return 0;
+
+ if (t->current_feature_level <= dns_server_possible_feature_level(t->server))
+ return 0;
+
+ /* The server's current feature level is lower than when we sent the original query. We learnt something from
+ the response or possibly an auxiliary DNSSEC response that we didn't know before. We take that as reason to
+ restart the whole transaction. This is a good idea to deal with servers that respond rubbish if we include
+ OPT RR or DO bit. One of these cases is documented here, for example:
+ https://open.nlnetlabs.nl/pipermail/dnssec-trigger/2014-November/000376.html */
+
+ log_debug("Server feature level is now lower than when we began our transaction. Restarting with new ID.");
+ dns_transaction_shuffle_id(t);
+ return dns_transaction_go(t);
}
static int on_stream_complete(DnsStream *s, int error) {
@@ -371,6 +444,13 @@ static int on_stream_complete(DnsStream *s, int error) {
if (ERRNO_IS_DISCONNECT(error)) {
usec_t usec;
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the
+ * question on this scope. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
+
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level, usec - t->start_usec);
@@ -379,7 +459,8 @@ static int on_stream_complete(DnsStream *s, int error) {
return 0;
}
if (error != 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ t->answer_errno = error;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
return 0;
}
@@ -529,22 +610,93 @@ static bool dns_transaction_dnssec_is_live(DnsTransaction *t) {
return false;
}
+static int dns_transaction_dnssec_ready(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+
+ assert(t);
+
+ /* Checks whether the auxiliary DNSSEC transactions of our transaction have completed, or are still
+ * ongoing. Returns 0, if we aren't ready for the DNSSEC validation, positive if we are. */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ switch (dt->state) {
+
+ case DNS_TRANSACTION_NULL:
+ case DNS_TRANSACTION_PENDING:
+ case DNS_TRANSACTION_VALIDATING:
+ /* Still ongoing */
+ return 0;
+
+ case DNS_TRANSACTION_RCODE_FAILURE:
+ if (dt->answer_rcode != DNS_RCODE_NXDOMAIN) {
+ log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode));
+ goto fail;
+ }
+
+ /* Fall-through: NXDOMAIN is good enough for us. This is because some DNS servers erronously
+ * return NXDOMAIN for empty non-terminals (Akamai...), and we need to handle that nicely, when
+ * asking for parent SOA or similar RRs to make unsigned proofs. */
+
+ case DNS_TRANSACTION_SUCCESS:
+ /* All good. */
+ break;
+
+ case DNS_TRANSACTION_DNSSEC_FAILED:
+ /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC
+ * validationr result */
+
+ log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result));
+ t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return 0;
+
+
+ default:
+ log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(dt->state));
+ goto fail;
+ }
+ }
+
+ /* All is ready, we can go and validate */
+ return 1;
+
+fail:
+ t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY;
+ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ return 0;
+}
+
static void dns_transaction_process_dnssec(DnsTransaction *t) {
int r;
assert(t);
/* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
- if (dns_transaction_dnssec_is_live(t))
+ r = dns_transaction_dnssec_ready(t);
+ if (r < 0)
+ goto fail;
+ if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */
+ return;
+
+ /* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better
+ * restart the lookup immediately. */
+ r = dns_transaction_maybe_restart(t);
+ if (r < 0)
+ goto fail;
+ if (r > 0) /* Transaction got restarted... */
return;
/* All our auxiliary DNSSEC transactions are complete now. Try
* to validate our RRset now. */
r = dns_transaction_validate_dnssec(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r == -EBADMSG) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
+ if (r < 0)
+ goto fail;
if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
t->scope->dnssec_mode == DNSSEC_YES) {
@@ -563,12 +715,70 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
return;
}
+ if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER)
+ dns_server_warn_downgrade(t->server);
+
dns_transaction_cache_answer(t);
if (t->answer_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
+
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+}
+
+static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
+ int r;
+
+ assert(t);
+
+ /* Checks whether the answer is positive, i.e. either a direct
+ * answer to the question, or a CNAME/DNAME for it */
+
+ r = dns_answer_match_key(t->answer, t->key, flags);
+ if (r != 0)
+ return r;
+
+ r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
+ if (r != 0)
+ return r;
+
+ return false;
+}
+
+static int dns_transaction_fix_rcode(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ /* Fix up the RCODE to SUCCESS if we get at least one matching RR in a response. Note that this contradicts the
+ * DNS RFCs a bit. Specifically, RFC 6604 Section 3 clarifies that the RCODE shall say something about a
+ * CNAME/DNAME chain element coming after the last chain element contained in the message, and not the first
+ * one included. However, it also indicates that not all DNS servers implement this correctly. Moreover, when
+ * using DNSSEC we usually only can prove the first element of a CNAME/DNAME chain anyway, hence let's settle
+ * on always processing the RCODE as referring to the immediate look-up we do, i.e. the first element of a
+ * CNAME/DNAME chain. This way, we uniformly handle CNAME/DNAME chains, regardless if the DNS server
+ * incorrectly implements RCODE, whether DNSSEC is in use, or whether the DNS server only supplied us with an
+ * incomplete CNAME/DNAME chain.
+ *
+ * Or in other words: if we get at least one positive reply in a message we patch NXDOMAIN to become SUCCESS,
+ * and then rely on the CNAME chasing logic to figure out that there's actually a CNAME error with a new
+ * lookup. */
+
+ if (t->answer_rcode != DNS_RCODE_NXDOMAIN)
+ return 0;
+
+ r = dns_transaction_has_positive_answer(t, NULL);
+ if (r <= 0)
+ return r;
+
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ return 0;
}
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
@@ -675,8 +885,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
return;
} else if (DNS_PACKET_TC(p))
dns_server_packet_truncated(t->server, t->current_feature_level);
- else
- dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size);
break;
@@ -714,10 +922,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (r < 0) {
/* On LLMNR, if we cannot connect to the host,
* we immediately give up */
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ goto fail;
/* On DNS, couldn't send? Try immediately again, with a new server */
dns_transaction_retry(t);
@@ -726,21 +932,34 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
return;
}
- /* Parse message, if it isn't parsed yet. */
+ /* After the superficial checks, actually parse the message. */
r = dns_packet_extract(p);
if (r < 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
+ /* Report that the OPT RR was missing */
+ if (t->server) {
+ if (!p->opt)
+ dns_server_packet_bad_opt(t->server, t->current_feature_level);
+
+ dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size);
+ }
+
+ /* See if we know things we didn't know before that indicate we better restart the lookup immediately. */
+ r = dns_transaction_maybe_restart(t);
+ if (r < 0)
+ goto fail;
+ if (r > 0) /* Transaction got restarted... */
+ return;
+
if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
/* Only consider responses with equivalent query section to the request */
r = dns_packet_is_reply_for(p, t->key);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r == 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
@@ -753,6 +972,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
t->answer_authenticated = false;
+ r = dns_transaction_fix_rcode(t);
+ if (r < 0)
+ goto fail;
+
/* Block GC while starting requests for additional DNSSEC RRs */
t->block_gc++;
r = dns_transaction_request_dnssec_keys(t);
@@ -769,10 +992,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* quickly. */
if (t->state != DNS_TRANSACTION_PENDING)
return;
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) {
/* There are DNSSEC transactions pending now. Update the state accordingly. */
t->state = DNS_TRANSACTION_VALIDATING;
@@ -783,6 +1004,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
dns_transaction_process_dnssec(t);
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -808,7 +1034,8 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
return 0;
}
if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ t->answer_errno = -r;
return 0;
}
@@ -818,12 +1045,12 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
return 0;
}
if (r == 0) {
- log_debug("Received inappropriate DNS packet as response, ignoring: %m");
+ log_debug("Received inappropriate DNS packet as response, ignoring.");
return 0;
}
if (DNS_PACKET_ID(p) != t->id) {
- log_debug("Received packet with incorrect transaction ID, ignoring: %m");
+ log_debug("Received packet with incorrect transaction ID, ignoring.");
return 0;
}
@@ -945,6 +1172,14 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
dns_transaction_stop_timeout(t);
+ r = dns_scope_network_good(t->scope);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN);
+ return 0;
+ }
+
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
@@ -961,6 +1196,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
t->start_usec = ts;
dns_transaction_reset_answer(t);
+ dns_transaction_flush_dnssec_transactions(t);
/* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
@@ -1301,11 +1537,15 @@ int dns_transaction_go(DnsTransaction *t) {
dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
return 0;
}
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) {
+ /* On LLMNR, if we cannot connect to a host via TCP when doing revers lookups. This means we cannot
+ * answer this request with this protocol. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
if (r < 0) {
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return 0;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ return r;
/* Couldn't send? Try immediately again, with a new server */
dns_scope_next_dns_server(t->scope);
@@ -1448,25 +1688,6 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
return 1;
}
-static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
- int r;
-
- assert(t);
-
- /* Checks whether the answer is positive, i.e. either a direct
- * answer to the question, or a CNAME/DNAME for it */
-
- r = dns_answer_match_key(t->answer, t->key, flags);
- if (r != 0)
- return r;
-
- r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
- if (r != 0)
- return r;
-
- return false;
-}
-
static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
int r;
@@ -1529,33 +1750,13 @@ static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRec
/* Check if the specified RR is the "primary" response,
* i.e. either matches the question precisely or is a
- * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */
+ * CNAME/DNAME for it. */
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r != 0)
return r;
- r = dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
- if (r != 0)
- return r;
-
- if (rr->key->type == DNS_TYPE_NSEC3) {
- const char *p;
-
- p = DNS_RESOURCE_KEY_NAME(rr->key);
- r = dns_name_parent(&p);
- if (r < 0)
- return r;
- if (r > 0) {
- r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), p);
- if (r < 0)
- return r;
- if (r > 0)
- return true;
- }
- }
-
- return rr->key->type == DNS_TYPE_NSEC;
+ return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
}
static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
@@ -1774,6 +1975,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
if (r > 0)
continue;
+ r = dns_answer_has_dname_for_cname(t->answer, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
name = DNS_RESOURCE_KEY_NAME(rr->key);
r = dns_name_parent(&name);
if (r < 0)
@@ -1873,69 +2080,15 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
}
void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source) {
- int r;
-
assert(t);
assert(source);
- if (!IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
- return;
-
- /* Invoked whenever any of our auxiliary DNSSEC transactions
- completed its work. We copy any RRs from that transaction
- over into our list of validated keys -- but only if the
- answer is authenticated.
-
- Note that we fail our transaction if the auxiliary
- transaction failed, except on NXDOMAIN. This is because
- some broken DNS servers (Akamai...) will return NXDOMAIN
- for empty non-terminals. */
-
- switch (source->state) {
+ /* Invoked whenever any of our auxiliary DNSSEC transactions completed its work. If the state is still PENDING,
+ we are still in the loop that adds further DNSSEC transactions, hence don't check if we are ready yet. If
+ the state is VALIDATING however, we should check if we are complete now. */
- case DNS_TRANSACTION_DNSSEC_FAILED:
-
- log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(source->answer_dnssec_result));
- t->answer_dnssec_result = source->answer_dnssec_result; /* Copy error code over */
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
- break;
-
- case DNS_TRANSACTION_RCODE_FAILURE:
-
- if (source->answer_rcode != DNS_RCODE_NXDOMAIN) {
- log_debug("Auxiliary DNSSEC RR query failed with rcode=%i.", source->answer_rcode);
- goto fail;
- }
-
- /* fall-through: NXDOMAIN is good enough for us */
-
- case DNS_TRANSACTION_SUCCESS:
- if (source->answer_authenticated) {
- r = dns_answer_extend(&t->validated_keys, source->answer);
- if (r < 0) {
- log_error_errno(r, "Failed to merge validated DNSSEC key data: %m");
- goto fail;
- }
- }
-
- /* If the state is still PENDING, we are still in the loop
- * that adds further DNSSEC transactions, hence don't check if
- * we are ready yet. If the state is VALIDATING however, we
- * should check if we are complete now. */
- if (t->state == DNS_TRANSACTION_VALIDATING)
- dns_transaction_process_dnssec(t);
- break;
-
- default:
- log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(source->state));
- goto fail;
- }
-
- return;
-
-fail:
- t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY;
- dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+ if (t->state == DNS_TRANSACTION_VALIDATING)
+ dns_transaction_process_dnssec(t);
}
static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) {
@@ -1950,7 +2103,7 @@ static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) {
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, t->answer) {
- r = dnssec_verify_dnskey_search(rr, t->validated_keys);
+ r = dnssec_verify_dnskey_by_ds_search(rr, t->validated_keys);
if (r < 0)
return r;
if (r == 0)
@@ -2378,6 +2531,31 @@ static int dns_transaction_invalidate_revoked_keys(DnsTransaction *t) {
return 0;
}
+static int dns_transaction_copy_validated(DnsTransaction *t) {
+ DnsTransaction *dt;
+ Iterator i;
+ int r;
+
+ assert(t);
+
+ /* Copy all validated RRs from the auxiliary DNSSEC transactions into our set of validated RRs */
+
+ SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+ if (DNS_TRANSACTION_IS_LIVE(dt->state))
+ continue;
+
+ if (!dt->answer_authenticated)
+ continue;
+
+ r = dns_answer_extend(&t->validated_keys, dt->answer);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int dns_transaction_validate_dnssec(DnsTransaction *t) {
_cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL;
enum {
@@ -2416,7 +2594,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (!dns_transaction_dnssec_supported_full(t)) {
/* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
- log_debug("Cannot validate response, server lacks DNSSEC support.");
+ log_debug("Not validating response for %" PRIu16 ", server lacks DNSSEC support.", t->id);
return 0;
}
@@ -2428,13 +2606,18 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
+ /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */
+ r = dns_transaction_copy_validated(t);
+ if (r < 0)
+ return r;
+
/* Second, see if there are DNSKEYs we already know a
* validated DS for. */
r = dns_transaction_validate_dnskey_by_ds(t);
if (r < 0)
return r;
- /* Third, remove all DNSKEY and DS RRs again that our trust
+ /* Fourth, remove all DNSKEY and DS RRs again that our trust
* anchor says are revoked. After all we might have marked
* some keys revoked above, but they might still be lingering
* in our validated_keys list. */
@@ -2516,7 +2699,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_secure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2531,28 +2714,22 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (result == DNSSEC_VALIDATED_WILDCARD) {
bool authenticated = false;
- const char *suffix;
+ const char *source;
- /* This RRset validated, but as a wildcard. This means we need to proof via NSEC/NSEC3
- * that no matching non-wildcard RR exists.
- *
- * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4*/
-
- r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
- if (r < 0)
- return r;
- if (r == 0)
- return -EBADMSG;
+ /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3
+ * that no matching non-wildcard RR exists.*/
- r = dns_name_parent(&suffix);
+ /* First step, determine the source of synthesis */
+ r = dns_resource_record_source(rrsig, &source);
if (r < 0)
return r;
- if (r == 0)
- return -EBADMSG;
- r = dnssec_nsec_test_between(validated, DNS_RESOURCE_KEY_NAME(rr->key), suffix, &authenticated);
- if (r < 0)
- return r;
+ r = dnssec_test_positive_wildcard(
+ validated,
+ DNS_RESOURCE_KEY_NAME(rr->key),
+ source,
+ rrsig->rrsig.signer,
+ &authenticated);
/* Unless the NSEC proof showed that the key really doesn't exist something is off. */
if (r == 0)
@@ -2562,10 +2739,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- if (authenticated)
- t->scope->manager->n_dnssec_secure++;
- else
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2584,7 +2758,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2596,7 +2770,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
/* This is an RR we know has to be signed. If it isn't this means
* the server is not attaching RRSIGs, hence complain. */
- dns_server_packet_rrsig_missing(t->server);
+ dns_server_packet_rrsig_missing(t->server, t->current_feature_level);
if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
@@ -2606,7 +2780,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2632,7 +2806,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2654,35 +2828,48 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
}
- if (IN_SET(result,
- DNSSEC_INVALID,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_NO_SIGNATURE))
- t->scope->manager->n_dnssec_bogus++;
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
- t->scope->manager->n_dnssec_indeterminate++;
-
r = dns_transaction_is_primary_response(t, rr);
if (r < 0)
return r;
if (r > 0) {
- /* This is a primary response
- * to our question, and it
- * failed validation. That's
- * fatal. */
- t->answer_dnssec_result = result;
- return 0;
+
+ /* Look for a matching DNAME for this CNAME */
+ r = dns_answer_has_dname_for_cname(t->answer, rr);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Also look among the stuff we already validated */
+ r = dns_answer_has_dname_for_cname(validated, rr);
+ if (r < 0)
+ return r;
+ }
+
+ if (r == 0) {
+ if (IN_SET(result,
+ DNSSEC_INVALID,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_NO_SIGNATURE))
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
+ else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
+
+ /* This is a primary response to our question, and it failed validation. That's
+ * fatal. */
+ t->answer_dnssec_result = result;
+ return 0;
+ }
+
+ /* This is a primary response, but we do have a DNAME RR in the RR that can replay this
+ * CNAME, hence rely on that, and we can remove the CNAME in favour of it. */
}
- /* This is just some auxiliary
- * data. Just remove the RRset and
- * continue. */
+ /* This is just some auxiliary data. Just remove the RRset and continue. */
r = dns_answer_remove_by_key(&t->answer, rr->key);
if (r < 0)
return r;
@@ -2753,6 +2940,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_NXDOMAIN;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NODATA:
@@ -2761,6 +2950,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_SUCCESS;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_OPTOUT:
@@ -2768,6 +2959,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NO_RR:
@@ -2776,11 +2969,13 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
r = dns_transaction_requires_nsec(t);
if (r < 0)
return r;
- if (r > 0)
+ if (r > 0) {
t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
- else {
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
+ } else {
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
}
break;
@@ -2788,12 +2983,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
/* We don't know the NSEC3 algorithm used? */
t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key);
break;
case DNSSEC_NSEC_FOUND:
case DNSSEC_NSEC_CNAME:
/* NSEC says it needs to be there, but we couldn't find it? Bummer! */
t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
break;
default:
@@ -2825,11 +3022,13 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
[DNS_TRANSACTION_TIMEOUT] = "timeout",
[DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
- [DNS_TRANSACTION_RESOURCES] = "resources",
+ [DNS_TRANSACTION_ERRNO] = "errno",
[DNS_TRANSACTION_ABORTED] = "aborted",
[DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
[DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor",
[DNS_TRANSACTION_RR_TYPE_UNSUPPORTED] = "rr-type-unsupported",
+ [DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
+ [DNS_TRANSACTION_NOT_FOUND] = "not-found",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 76cf6e71db..b6c5b2861c 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -35,11 +35,13 @@ enum DnsTransactionState {
DNS_TRANSACTION_TIMEOUT,
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
DNS_TRANSACTION_INVALID_REPLY,
- DNS_TRANSACTION_RESOURCES,
+ DNS_TRANSACTION_ERRNO,
DNS_TRANSACTION_ABORTED,
DNS_TRANSACTION_DNSSEC_FAILED,
DNS_TRANSACTION_NO_TRUST_ANCHOR,
DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -1
};
@@ -82,6 +84,7 @@ struct DnsTransaction {
DnssecResult answer_dnssec_result;
DnsTransactionSource answer_source;
uint32_t answer_nsec_ttl;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Indicates whether the primary answer is authenticated,
* i.e. whether the RRs from answer which directly match the
diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c
index 9bee44b5c7..02d7ac91e1 100644
--- a/src/resolve/resolved-dns-trust-anchor.c
+++ b/src/resolve/resolved-dns-trust-anchor.c
@@ -665,7 +665,7 @@ static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceReco
* DS fingerprint will be the one of the
* unrevoked DNSKEY, but the one we got passed
* here has the bit set. */
- r = dnssec_verify_dnskey(revoked_dnskey, anchor, true);
+ r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c
new file mode 100644
index 0000000000..ee82c96822
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.c
@@ -0,0 +1,448 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "hostname-util.h"
+#include "resolved-etc-hosts.h"
+#include "resolved-dns-synthesize.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+/* Recheck /etc/hosts at most once every 2s */
+#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
+
+typedef struct EtcHostsItem {
+ int family;
+ union in_addr_union address;
+
+ char **names;
+} EtcHostsItem;
+
+typedef struct EtcHostsItemByName {
+ char *name;
+
+ EtcHostsItem **items;
+ size_t n_items, n_allocated;
+} EtcHostsItemByName;
+
+void manager_etc_hosts_flush(Manager *m) {
+ EtcHostsItem *item;
+ EtcHostsItemByName *bn;
+
+ while ((item = set_steal_first(m->etc_hosts_by_address))) {
+ strv_free(item->names);
+ free(item);
+ }
+
+ while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
+ free(bn->name);
+ free(bn->items);
+ free(bn);
+ }
+
+ m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
+ m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
+
+ m->etc_hosts_mtime = USEC_INFINITY;
+}
+
+static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
+ const EtcHostsItem *item = p;
+
+ siphash24_compress(&item->family, sizeof(item->family), state);
+
+ if (item->family == AF_INET)
+ siphash24_compress(&item->address.in, sizeof(item->address.in), state);
+ else if (item->family == AF_INET6)
+ siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
+}
+
+static int etc_hosts_item_compare_func(const void *a, const void *b) {
+ const EtcHostsItem *x = a, *y = b;
+
+ if (x->family != y->family)
+ return x->family - y->family;
+
+ if (x->family == AF_INET)
+ return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
+
+ if (x->family == AF_INET6)
+ return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
+
+ return trivial_compare_func(a, b);
+}
+
+static const struct hash_ops etc_hosts_item_ops = {
+ .hash = etc_hosts_item_hash_func,
+ .compare = etc_hosts_item_compare_func,
+};
+
+static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
+
+ EtcHostsItem key = {
+ .family = family,
+ .address = *address,
+ };
+ EtcHostsItem *item;
+ char **n;
+ int r;
+
+ assert(m);
+ assert(address);
+
+ r = in_addr_is_null(family, address);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to
+ * nothing. */
+ item = NULL;
+ else {
+ /* If this is a normal address, then, simply add entry mapping it to the specified names */
+
+ item = set_get(m->etc_hosts_by_address, &key);
+ if (item) {
+ r = strv_extend_strv(&item->names, names, true);
+ if (r < 0)
+ return log_oom();
+ } else {
+
+ r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
+ if (r < 0)
+ return log_oom();
+
+ item = new0(EtcHostsItem, 1);
+ if (!item)
+ return log_oom();
+
+ item->family = family;
+ item->address = *address;
+ item->names = names;
+
+ r = set_put(m->etc_hosts_by_address, item);
+ if (r < 0) {
+ free(item);
+ return log_oom();
+ }
+ }
+ }
+
+ STRV_FOREACH(n, names) {
+ EtcHostsItemByName *bn;
+
+ bn = hashmap_get(m->etc_hosts_by_name, *n);
+ if (!bn) {
+ r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ bn = new0(EtcHostsItemByName, 1);
+ if (!bn)
+ return log_oom();
+
+ bn->name = strdup(*n);
+ if (!bn->name) {
+ free(bn);
+ return log_oom();
+ }
+
+ r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
+ if (r < 0) {
+ free(bn->name);
+ free(bn);
+ return log_oom();
+ }
+ }
+
+ if (item) {
+ if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
+ return log_oom();
+
+ bn->items[bn->n_items++] = item;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_line(Manager *m, unsigned nr, const char *line) {
+ _cleanup_free_ char *address = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ union in_addr_union in;
+ bool suppressed = false;
+ int family, r;
+
+ assert(m);
+ assert(line);
+
+ r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
+ if (r == 0) {
+ log_error("Premature end of line, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ r = in_addr_from_string_auto(address, &family, &in);
+ if (r < 0)
+ return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
+
+ for (;;) {
+ _cleanup_free_ char *name = NULL;
+
+ r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r <= 0)
+ return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+
+ if (is_localhost(name)) {
+ /* Suppress the "localhost" line that is often seen */
+ suppressed = true;
+ continue;
+ }
+
+ r = strv_push(&names, name);
+ if (r < 0)
+ return log_oom();
+
+ name = NULL;
+ }
+
+ if (strv_isempty(names)) {
+
+ if (suppressed)
+ return 0;
+
+ log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ /* Takes possession of the names strv */
+ r = add_item(m, family, &in, names);
+ if (r < 0)
+ return r;
+
+ names = NULL;
+ return r;
+}
+
+int manager_etc_hosts_read(Manager *m) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ struct stat st;
+ usec_t ts;
+ unsigned nr = 0;
+ int r;
+
+ assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* See if we checked /etc/hosts recently already */
+ if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts)
+ return 0;
+
+ m->etc_hosts_last = ts;
+
+ if (m->etc_hosts_mtime != USEC_INFINITY) {
+ if (stat("/etc/hosts", &st) < 0) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
+ }
+
+ /* Did the mtime change? If not, there's no point in re-reading the file. */
+ if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime)
+ return 0;
+ }
+
+ f = fopen("/etc/hosts", "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to open /etc/hosts: %m");
+ }
+
+ /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
+ * invocation */
+ r = fstat(fileno(f), &st);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
+
+ manager_etc_hosts_flush(m);
+
+ FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
+ char *l;
+
+ nr ++;
+
+ l = strstrip(line);
+ if (isempty(l))
+ continue;
+ if (l[0] == '#')
+ continue;
+
+ r = parse_line(m, nr, l);
+ if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
+ goto clear;
+ }
+
+ m->etc_hosts_mtime = timespec_load(&st.st_mtim);
+ m->etc_hosts_last = ts;
+
+ return 1;
+
+clear:
+ manager_etc_hosts_flush(m);
+ return r;
+}
+
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
+ bool found_a = false, found_aaaa = false;
+ EtcHostsItemByName *bn;
+ EtcHostsItem k = {};
+ DnsResourceKey *t;
+ const char *name;
+ unsigned i;
+ int r;
+
+ assert(m);
+ assert(q);
+ assert(answer);
+
+ r = manager_etc_hosts_read(m);
+ if (r < 0)
+ return r;
+
+ name = dns_question_first_name(q);
+ if (!name)
+ return 0;
+
+ r = dns_name_address(name, &k.family, &k.address);
+ if (r > 0) {
+ EtcHostsItem *item;
+ DnsResourceKey *found_ptr = NULL;
+
+ item = set_get(m->etc_hosts_by_address, &k);
+ if (!item)
+ return 0;
+
+ /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
+ * we'll only return if the request was for PTR. */
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found_ptr = t;
+ break;
+ }
+ }
+
+ if (found_ptr) {
+ char **n;
+
+ r = dns_answer_reserve(answer, strv_length(item->names));
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(n, item->names) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new(found_ptr);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(*n);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 1;
+ }
+
+ bn = hashmap_get(m->etc_hosts_by_name, name);
+ if (!bn)
+ return 0;
+
+ r = dns_answer_reserve(answer, bn->n_items);
+ if (r < 0)
+ return r;
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY))
+ found_a = true;
+ if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ found_aaaa = true;
+
+ if (found_a && found_aaaa)
+ break;
+ }
+
+ for (i = 0; i < bn->n_items; i++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ if ((found_a && bn->items[i]->family != AF_INET) &&
+ (found_aaaa && bn->items[i]->family != AF_INET6))
+ continue;
+
+ r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h
new file mode 100644
index 0000000000..9d5a175f18
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-manager.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-answer.h"
+
+void manager_etc_hosts_flush(Manager *m);
+int manager_etc_hosts_read(Manager *m);
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf
index c5ad04afd7..82f26215df 100644
--- a/src/resolve/resolved-gperf.gperf
+++ b/src/resolve/resolved-gperf.gperf
@@ -18,5 +18,4 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
Resolve.Domains, config_parse_search_domains, 0, 0
Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
-Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
new file mode 100644
index 0000000000..e6b087f412
--- /dev/null
+++ b/src/resolve/resolved-link-bus.c
@@ -0,0 +1,552 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "bus-util.h"
+#include "parse-util.h"
+#include "resolve-util.h"
+#include "resolved-bus.h"
+#include "resolved-link-bus.h"
+#include "strv.h"
+
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_dnssec_mode, dnssec_mode, DnssecMode);
+
+static int property_get_dns(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ DnsServer *s;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "(iay)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(servers, s, l->dns_servers) {
+ r = bus_dns_server_append(reply, s, false);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_domains(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ DnsSearchDomain *d;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(domains, d, l->search_domains) {
+ r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_scopes_mask(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ uint64_t mask;
+
+ assert(reply);
+ assert(l);
+
+ mask = (l->unicast_scope ? SD_RESOLVED_DNS : 0) |
+ (l->llmnr_ipv4_scope ? SD_RESOLVED_LLMNR_IPV4 : 0) |
+ (l->llmnr_ipv6_scope ? SD_RESOLVED_LLMNR_IPV6 : 0) |
+ (l->mdns_ipv4_scope ? SD_RESOLVED_MDNS_IPV4 : 0) |
+ (l->mdns_ipv6_scope ? SD_RESOLVED_MDNS_IPV6 : 0);
+
+ return sd_bus_message_append(reply, "t", mask);
+}
+
+static int property_get_ntas(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+ const char *name;
+ Iterator i;
+ int r;
+
+ assert(reply);
+ assert(l);
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(name, l->dnssec_negative_trust_anchors, i) {
+ r = sd_bus_message_append(reply, "s", name);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_dnssec_supported(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = userdata;
+
+ assert(reply);
+ assert(l);
+
+ return sd_bus_message_append(reply, "b", link_dnssec_supported(l));
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ struct in_addr_data *dns = NULL;
+ size_t allocated = 0, n = 0;
+ Link *l = userdata;
+ unsigned i;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_enter_container(message, 'a', "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ int family;
+ size_t sz;
+ const void *d;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_enter_container(message, 'r', "iay");
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1))
+ return -ENOMEM;
+
+ dns[n].family = family;
+ memcpy(&dns[n].address, d, sz);
+ n++;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ dns_server_mark_all(l->dns_servers);
+
+ for (i = 0; i < n; i++) {
+ DnsServer *s;
+
+ s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address);
+ if (s)
+ dns_server_move_back_and_unmark(s);
+ else {
+ r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address);
+ if (r < 0)
+ goto clear;
+ }
+
+ }
+
+ dns_server_unlink_marked(l->dns_servers);
+ link_allocate_scopes(l);
+
+ return sd_bus_reply_method_return(message, NULL);
+
+clear:
+ dns_server_unlink_all(l->dns_servers);
+ return r;
+}
+
+int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_enter_container(message, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *name;
+ int route_only;
+
+ r = sd_bus_message_read(message, "(sb)", &name, &route_only);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
+ if (!route_only && dns_name_is_root(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ }
+
+ dns_search_domain_mark_all(l->search_domains);
+
+ r = sd_bus_message_rewind(message, false);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ DnsSearchDomain *d;
+ const char *name;
+ int route_only;
+
+ r = sd_bus_message_read(message, "(sb)", &name, &route_only);
+ if (r < 0)
+ goto clear;
+ if (r == 0)
+ break;
+
+ r = dns_search_domain_find(l->search_domains, name, &d);
+ if (r < 0)
+ goto clear;
+
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(d);
+ else {
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
+ if (r < 0)
+ goto clear;
+ }
+
+ d->route_only = route_only;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ goto clear;
+
+ dns_search_domain_unlink_marked(l->search_domains);
+ return sd_bus_reply_method_return(message, NULL);
+
+clear:
+ dns_search_domain_unlink_all(l->search_domains);
+ return r;
+}
+
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *llmnr;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &llmnr);
+ if (r < 0)
+ return r;
+
+ if (isempty(llmnr))
+ mode = RESOLVE_SUPPORT_YES;
+ else {
+ mode = resolve_support_from_string(llmnr);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
+ }
+
+ l->llmnr_support = mode;
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ ResolveSupport mode;
+ const char *mdns;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &mdns);
+ if (r < 0)
+ return r;
+
+ if (isempty(mdns))
+ mode = RESOLVE_SUPPORT_NO;
+ else {
+ mode = resolve_support_from_string(mdns);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
+ }
+
+ l->mdns_support = mode;
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ const char *dnssec;
+ DnssecMode mode;
+ int r;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read(message, "s", &dnssec);
+ if (r < 0)
+ return r;
+
+ if (isempty(dnssec))
+ mode = _DNSSEC_MODE_INVALID;
+ else {
+ mode = dnssec_mode_from_string(dnssec);
+ if (mode < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
+ }
+
+ link_set_dnssec_mode(l, mode);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_set_free_free_ Set *ns = NULL;
+ _cleanup_free_ char **ntas = NULL;
+ Link *l = userdata;
+ int r;
+ char **i;
+
+ assert(message);
+ assert(l);
+
+ r = sd_bus_message_read_strv(message, &ntas);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, ntas) {
+ r = dns_name_is_valid(*i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
+ }
+
+ ns = set_new(&dns_name_hash_ops);
+ if (!ns)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, ntas) {
+ r = set_put_strdup(ns, *i);
+ if (r < 0)
+ return r;
+ }
+
+ set_free_free(l->dnssec_negative_trust_anchors);
+ l->dnssec_negative_trust_anchors = ns;
+ ns = NULL;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+
+ assert(message);
+ assert(l);
+
+ link_flush_settings(l);
+ link_allocate_scopes(l);
+ link_add_rrs(l, false);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+const sd_bus_vtable link_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
+ SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
+ SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
+ SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0),
+ SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0),
+ SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0),
+ SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
+ SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0),
+
+ SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
+ SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_search_domains, 0),
+ SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
+ SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
+ SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0),
+ SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0),
+ SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0),
+
+ SD_BUS_VTABLE_END
+};
+
+int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ Manager *m = userdata;
+ int ifindex;
+ Link *link;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(interface);
+ assert(found);
+ assert(m);
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/resolve1/link", &e);
+ if (r <= 0)
+ return 0;
+
+ r = parse_ifindex(e, &ifindex);
+ if (r < 0)
+ return 0;
+
+ link = hashmap_get(m->links, INT_TO_PTR(ifindex));
+ if (!link)
+ return 0;
+
+ *found = link;
+ return 1;
+}
+
+char *link_bus_path(Link *link) {
+ _cleanup_free_ char *ifindex = NULL;
+ char *p;
+ int r;
+
+ assert(link);
+
+ if (asprintf(&ifindex, "%i", link->ifindex) < 0)
+ return NULL;
+
+ r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifindex, &p);
+ if (r < 0)
+ return NULL;
+
+ return p;
+}
+
+int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ Link *link;
+ Iterator i;
+ unsigned c = 0;
+
+ assert(bus);
+ assert(path);
+ assert(m);
+ assert(nodes);
+
+ l = new0(char*, hashmap_size(m->links) + 1);
+ if (!l)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(link, m->links, i) {
+ char *p;
+
+ p = link_bus_path(link);
+ if (!p)
+ return -ENOMEM;
+
+ l[c++] = p;
+ }
+
+ l[c] = NULL;
+ *nodes = l;
+ l = NULL;
+
+ return 1;
+}
diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h
new file mode 100644
index 0000000000..d444957d1c
--- /dev/null
+++ b/src/resolve/resolved-link-bus.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+#include "resolved-link.h"
+
+extern const sd_bus_vtable link_vtable[];
+
+int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+char *link_bus_path(Link *link);
+int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 928307e004..37dd4a6e78 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -49,6 +49,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
l->llmnr_support = RESOLVE_SUPPORT_YES;
l->mdns_support = RESOLVE_SUPPORT_NO;
l->dnssec_mode = _DNSSEC_MODE_INVALID;
+ l->operstate = IF_OPER_UNKNOWN;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
@@ -63,15 +64,27 @@ int link_new(Manager *m, Link **ret, int ifindex) {
return 0;
}
+void link_flush_settings(Link *l) {
+ assert(l);
+
+ l->llmnr_support = RESOLVE_SUPPORT_YES;
+ l->mdns_support = RESOLVE_SUPPORT_NO;
+ l->dnssec_mode = _DNSSEC_MODE_INVALID;
+
+ dns_server_unlink_all(l->dns_servers);
+ dns_search_domain_unlink_all(l->search_domains);
+
+ l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
+}
+
Link *link_free(Link *l) {
if (!l)
return NULL;
- dns_server_unlink_all(l->dns_servers);
- dns_search_domain_unlink_all(l->search_domains);
+ link_flush_settings(l);
while (l->addresses)
- link_address_free(l->addresses);
+ (void) link_address_free(l->addresses);
if (l->manager)
hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
@@ -82,18 +95,17 @@ Link *link_free(Link *l) {
dns_scope_free(l->mdns_ipv4_scope);
dns_scope_free(l->mdns_ipv6_scope);
- set_free_free(l->dnssec_negative_trust_anchors);
-
free(l);
return NULL;
}
-static void link_allocate_scopes(Link *l) {
+void link_allocate_scopes(Link *l) {
int r;
assert(l);
- if (l->dns_servers) {
+ if (link_relevant(l, AF_UNSPEC, false) &&
+ l->dns_servers) {
if (!l->unicast_scope) {
r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
@@ -102,7 +114,7 @@ static void link_allocate_scopes(Link *l) {
} else
l->unicast_scope = dns_scope_free(l->unicast_scope);
- if (link_relevant(l, AF_INET) &&
+ if (link_relevant(l, AF_INET, true) &&
l->llmnr_support != RESOLVE_SUPPORT_NO &&
l->manager->llmnr_support != RESOLVE_SUPPORT_NO) {
if (!l->llmnr_ipv4_scope) {
@@ -113,7 +125,7 @@ static void link_allocate_scopes(Link *l) {
} else
l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
- if (link_relevant(l, AF_INET6) &&
+ if (link_relevant(l, AF_INET6, true) &&
l->llmnr_support != RESOLVE_SUPPORT_NO &&
l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
socket_ipv6_is_supported()) {
@@ -125,7 +137,7 @@ static void link_allocate_scopes(Link *l) {
} else
l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
- if (link_relevant(l, AF_INET) &&
+ if (link_relevant(l, AF_INET, true) &&
l->mdns_support != RESOLVE_SUPPORT_NO &&
l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv4_scope) {
@@ -136,7 +148,7 @@ static void link_allocate_scopes(Link *l) {
} else
l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
- if (link_relevant(l, AF_INET6) &&
+ if (link_relevant(l, AF_INET6, true) &&
l->mdns_support != RESOLVE_SUPPORT_NO &&
l->manager->mdns_support != RESOLVE_SUPPORT_NO) {
if (!l->mdns_ipv6_scope) {
@@ -166,7 +178,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
if (r < 0)
return r;
- sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
strncpy(l->name, n, sizeof(l->name)-1);
@@ -277,8 +290,29 @@ clear:
return r;
}
+void link_set_dnssec_mode(Link *l, DnssecMode mode) {
+
+ assert(l);
+
+ if (l->dnssec_mode == mode)
+ return;
+
+ if ((l->dnssec_mode == _DNSSEC_MODE_INVALID) ||
+ (l->dnssec_mode == DNSSEC_NO && mode != DNSSEC_NO) ||
+ (l->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE && mode == DNSSEC_YES)) {
+
+ /* When switching from non-DNSSEC mode to DNSSEC mode, flush the cache. Also when switching from the
+ * allow-downgrade mode to full DNSSEC mode, flush it too. */
+ if (l->unicast_scope)
+ dns_cache_flush(&l->unicast_scope->cache);
+ }
+
+ l->dnssec_mode = mode;
+}
+
static int link_update_dnssec_mode(Link *l) {
_cleanup_free_ char *m = NULL;
+ DnssecMode mode;
int r;
assert(l);
@@ -291,12 +325,14 @@ static int link_update_dnssec_mode(Link *l) {
if (r < 0)
goto clear;
- l->dnssec_mode = dnssec_mode_from_string(m);
- if (l->dnssec_mode < 0) {
+ mode = dnssec_mode_from_string(m);
+ if (mode < 0) {
r = -EINVAL;
goto clear;
}
+ link_set_dnssec_mode(l, mode);
+
return 0;
clear:
@@ -341,38 +377,60 @@ clear:
return r;
}
+static int link_update_search_domain_one(Link *l, const char *name, bool route_only) {
+ DnsSearchDomain *d;
+ int r;
+
+ r = dns_search_domain_find(l->search_domains, name, &d);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(d);
+ else {
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
+ if (r < 0)
+ return r;
+ }
+
+ d->route_only = route_only;
+ return 0;
+}
+
static int link_update_search_domains(Link *l) {
- _cleanup_strv_free_ char **domains = NULL;
+ _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL;
char **i;
- int r;
+ int r, q;
assert(l);
- r = sd_network_link_get_domains(l->ifindex, &domains);
- if (r == -ENODATA) {
+ r = sd_network_link_get_search_domains(l->ifindex, &sdomains);
+ if (r < 0 && r != -ENODATA)
+ goto clear;
+
+ q = sd_network_link_get_route_domains(l->ifindex, &rdomains);
+ if (q < 0 && q != -ENODATA) {
+ r = q;
+ goto clear;
+ }
+
+ if (r == -ENODATA && q == -ENODATA) {
/* networkd knows nothing about this interface, and that's fine. */
r = 0;
goto clear;
}
- if (r < 0)
- goto clear;
dns_search_domain_mark_all(l->search_domains);
- STRV_FOREACH(i, domains) {
- DnsSearchDomain *d;
-
- r = dns_search_domain_find(l->search_domains, *i, &d);
+ STRV_FOREACH(i, sdomains) {
+ r = link_update_search_domain_one(l, *i, false);
if (r < 0)
goto clear;
+ }
- if (r > 0)
- dns_search_domain_move_back_and_unmark(d);
- else {
- r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
- if (r < 0)
- goto clear;
- }
+ STRV_FOREACH(i, rdomains) {
+ r = link_update_search_domain_one(l, *i, true);
+ if (r < 0)
+ goto clear;
}
dns_search_domain_unlink_marked(l->search_domains);
@@ -383,11 +441,45 @@ clear:
return r;
}
-int link_update_monitor(Link *l) {
+static int link_is_unmanaged(Link *l) {
+ _cleanup_free_ char *state = NULL;
int r;
assert(l);
+ r = sd_network_link_get_setup_state(l->ifindex, &state);
+ if (r == -ENODATA)
+ return 1;
+ if (r < 0)
+ return r;
+
+ return STR_IN_SET(state, "pending", "unmanaged");
+}
+
+static void link_read_settings(Link *l) {
+ int r;
+
+ assert(l);
+
+ /* Read settings from networkd, except when networkd is not managing this interface. */
+
+ r = link_is_unmanaged(l);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to determine whether interface %s is managed: %m", l->name);
+ return;
+ }
+ if (r > 0) {
+
+ /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */
+ if (l->is_managed)
+ link_flush_settings(l);
+
+ l->is_managed = false;
+ return;
+ }
+
+ l->is_managed = true;
+
r = link_update_dns_servers(l);
if (r < 0)
log_warning_errno(r, "Failed to read DNS servers for interface %s, ignoring: %m", l->name);
@@ -411,35 +503,52 @@ int link_update_monitor(Link *l) {
r = link_update_search_domains(l);
if (r < 0)
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
+}
+
+int link_update_monitor(Link *l) {
+ assert(l);
+ link_read_settings(l);
link_allocate_scopes(l);
link_add_rrs(l, false);
return 0;
}
-bool link_relevant(Link *l, int family) {
+bool link_relevant(Link *l, int family, bool multicast) {
_cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
- /* A link is relevant if it isn't a loopback or pointopoint
- * device, has a link beat, can do multicast and has at least
- * one relevant IP address */
+ /* A link is relevant for multicast traffic if it isn't a loopback or pointopoint device, has a link beat, can
+ * do multicast and has at least one relevant IP address */
+
+ if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
+ return false;
- if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT))
+ if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
return false;
- if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
+ if (multicast) {
+ if (l->flags & IFF_POINTOPOINT)
+ return false;
+
+ if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
+ return false;
+ }
+
+ /* Check kernel operstate
+ * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
+ if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
return false;
- sd_network_link_get_operational_state(l->ifindex, &state);
+ (void) sd_network_link_get_operational_state(l->ifindex, &state);
if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
return false;
LIST_FOREACH(addresses, a, l->addresses)
- if (a->family == family && link_address_relevant(a))
+ if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a))
return true;
return false;
@@ -500,6 +609,30 @@ void link_next_dns_server(Link *l) {
link_set_dns_server(l, l->dns_servers);
}
+DnssecMode link_get_dnssec_mode(Link *l) {
+ assert(l);
+
+ if (l->dnssec_mode != _DNSSEC_MODE_INVALID)
+ return l->dnssec_mode;
+
+ return manager_get_dnssec_mode(l->manager);
+}
+
+bool link_dnssec_supported(Link *l) {
+ DnsServer *server;
+
+ assert(l);
+
+ if (link_get_dnssec_mode(l) == DNSSEC_NO)
+ return false;
+
+ server = link_get_dns_server(l);
+ if (server)
+ return dns_server_dnssec_supported(server);
+
+ return true;
+}
+
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) {
LinkAddress *a;
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index db0e51da04..3b6aafb8f0 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -78,22 +78,32 @@ struct Link {
DnsScope *mdns_ipv4_scope;
DnsScope *mdns_ipv6_scope;
+ bool is_managed;
+
char name[IF_NAMESIZE];
uint32_t mtu;
+ uint8_t operstate;
};
int link_new(Manager *m, Link **ret, int ifindex);
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
-bool link_relevant(Link *l, int family);
+bool link_relevant(Link *l, int family, bool multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
+void link_flush_settings(Link *l);
+void link_set_dnssec_mode(Link *l, DnssecMode mode);
+void link_allocate_scopes(Link *l);
+
DnsServer* link_set_dns_server(Link *l, DnsServer *s);
DnsServer* link_get_dns_server(Link *l);
void link_next_dns_server(Link *l);
+DnssecMode link_get_dnssec_mode(Link *l);
+bool link_dnssec_supported(Link *l);
+
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index b17a19d331..4306403834 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -37,10 +37,11 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
+#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
-#include "resolved-resolv-conf.h"
#include "resolved-mdns.h"
+#include "resolved-resolv-conf.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -205,7 +206,7 @@ static int manager_rtnl_listen(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_attach_event(m->rtnl, m->event, 0);
+ r = sd_netlink_attach_event(m->rtnl, m->event, SD_EVENT_PRIORITY_IMPORTANT);
if (r < 0)
return r;
@@ -313,6 +314,10 @@ static int manager_network_monitor_listen(Manager *m) {
if (r < 0)
return r;
+ r = sd_event_source_set_priority(m->network_event_source, SD_EVENT_PRIORITY_IMPORTANT+5);
+ if (r < 0)
+ return r;
+
(void) sd_event_source_set_description(m->network_event_source, "network-monitor");
return 0;
@@ -485,6 +490,7 @@ int manager_new(Manager **ret) {
m->dnssec_mode = DNSSEC_NO;
m->read_resolv_conf = true;
m->need_builtin_fallbacks = true;
+ m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
@@ -594,6 +600,7 @@ Manager *manager_free(Manager *m) {
free(m->mdns_hostname);
dns_trust_anchor_flush(&m->trust_anchor);
+ manager_etc_hosts_flush(m);
free(m);
@@ -1173,3 +1180,49 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
return 0;
}
+
+DnssecMode manager_get_dnssec_mode(Manager *m) {
+ assert(m);
+
+ if (m->dnssec_mode != _DNSSEC_MODE_INVALID)
+ return m->dnssec_mode;
+
+ return DNSSEC_NO;
+}
+
+bool manager_dnssec_supported(Manager *m) {
+ DnsServer *server;
+ Iterator i;
+ Link *l;
+
+ assert(m);
+
+ if (manager_get_dnssec_mode(m) == DNSSEC_NO)
+ return false;
+
+ server = manager_get_dns_server(m);
+ if (server && !dns_server_dnssec_supported(server))
+ return false;
+
+ HASHMAP_FOREACH(l, m->links, i)
+ if (!link_dnssec_supported(l))
+ return false;
+
+ return true;
+}
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
+
+ assert(verdict >= 0);
+ assert(verdict < _DNSSEC_VERDICT_MAX);
+
+ if (log_get_max_level() >= LOG_DEBUG) {
+ _cleanup_free_ char *s = NULL;
+
+ (void) dns_resource_key_to_string(key, &s);
+
+ log_debug("Found verdict for lookup %s: %s", s ? strstrip(s) : "n/a", dnssec_verdict_to_string(verdict));
+ }
+
+ m->n_dnssec_verdict[verdict]++;
+}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 1907d2e1bc..1af49c8fb9 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -74,6 +74,7 @@ struct Manager {
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
+ bool permit_domain_search;
bool need_builtin_fallbacks:1;
@@ -123,7 +124,12 @@ struct Manager {
sd_event_source *sigusr1_event_source;
unsigned n_transactions_total;
- unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
+ unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
+
+ /* Data from /etc/hosts */
+ Set* etc_hosts_by_address;
+ Hashmap* etc_hosts_by_name;
+ usec_t etc_hosts_last, etc_hosts_mtime;
};
/* Manager */
@@ -158,3 +164,8 @@ int manager_is_own_hostname(Manager *m, const char *name);
int manager_compile_dns_servers(Manager *m, OrderedSet **servers);
int manager_compile_search_domains(Manager *m, OrderedSet **domains);
+
+DnssecMode manager_get_dnssec_mode(Manager *m);
+bool manager_dnssec_supported(Manager *m);
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in
index 0ba572d113..efc9c6733a 100644
--- a/src/resolve/resolved.conf.in
+++ b/src/resolve/resolved.conf.in
@@ -16,5 +16,4 @@
#FallbackDNS=@DNS_SERVERS@
#Domains=
#LLMNR=yes
-#MulticastDNS=no
#DNSSEC=no
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
new file mode 100644
index 0000000000..caac251e83
--- /dev/null
+++ b/src/resolve/test-dnssec-complex.c
@@ -0,0 +1,238 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ 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
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/ip.h>
+
+#include "sd-bus.h"
+
+#include "af-list.h"
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "dns-type.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "time-util.h"
+
+#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
+
+static void prefix_random(const char *name, char **ret) {
+ uint64_t i, u;
+ char *m = NULL;
+
+ u = 1 + (random_u64() & 3);
+
+ for (i = 0; i < u; i++) {
+ _cleanup_free_ char *b = NULL;
+ char *x;
+
+ assert_se(asprintf(&b, "x%" PRIu64 "x", random_u64()));
+ x = strjoin(b, ".", name, NULL);
+ assert_se(x);
+
+ free(m);
+ m = x;
+ }
+
+ *ret = m;
+ }
+
+static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const char *result) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *m = NULL;
+ int r;
+
+ /* If the name starts with a dot, we prefix one to three random labels */
+ if (startswith(name, ".")) {
+ prefix_random(name + 1, &m);
+ name = m;
+ }
+
+ assert_se(sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveRecord") >= 0);
+
+ assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+
+ if (r < 0) {
+ assert_se(result);
+ assert_se(sd_bus_error_has_name(&error, result));
+ log_info("[OK] %s/%s resulted in <%s>.", name, dns_type_to_string(type), error.name);
+ } else {
+ assert_se(!result);
+ log_info("[OK] %s/%s succeeded.", name, dns_type_to_string(type));
+ }
+}
+
+static void test_hostname_lookup(sd_bus *bus, const char *name, int family, const char *result) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *m = NULL;
+ const char *af;
+ int r;
+
+ af = family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family);
+
+ /* If the name starts with a dot, we prefix one to three random labels */
+ if (startswith(name, ".")) {
+ prefix_random(name + 1, &m);
+ name = m;
+ }
+
+ assert_se(sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveHostname") >= 0);
+
+ assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+
+ if (r < 0) {
+ assert_se(result);
+ assert_se(sd_bus_error_has_name(&error, result));
+ log_info("[OK] %s/%s resulted in <%s>.", name, af, error.name);
+ } else {
+ assert_se(!result);
+ log_info("[OK] %s/%s succeeded.", name, af);
+ }
+
+}
+
+int main(int argc, char* argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+ /* Note that this is a manual test as it requires:
+ *
+ * Full network access
+ * A DNSSEC capable DNS server
+ * That zones contacted are still set up as they were when I wrote this.
+ */
+
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ /* Normally signed */
+ test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, "www.eurid.eu", AF_UNSPEC, NULL);
+
+ test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, "sigok.verteiltesysteme.net", AF_UNSPEC, NULL);
+
+ /* Normally signed, NODATA */
+ test_rr_lookup(bus, "www.eurid.eu", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+ test_rr_lookup(bus, "sigok.verteiltesysteme.net", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* Invalid signature */
+ test_rr_lookup(bus, "sigfail.verteiltesysteme.net", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, "sigfail.verteiltesysteme.net", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* Invalid signature, RSA, wildcard */
+ test_rr_lookup(bus, ".wilda.rhybar.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, ".wilda.rhybar.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* Invalid signature, ECDSA, wildcard */
+ test_rr_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", DNS_TYPE_A, BUS_ERROR_DNSSEC_FAILED);
+ test_hostname_lookup(bus, ".wilda.rhybar.ecdsa.0skar.cz", AF_INET, BUS_ERROR_DNSSEC_FAILED);
+
+ /* NXDOMAIN in NSEC domain */
+ test_rr_lookup(bus, "hhh.nasa.gov", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "hhh.nasa.gov", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* wildcard, NSEC zone */
+ test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wilda.nsec.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC zone, NODATA */
+ test_rr_lookup(bus, ".wilda.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC3 zone */
+ test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wilda.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC3 zone, NODATA */
+ test_rr_lookup(bus, ".wilda.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC zone, CNAME */
+ test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, ".wild.nsec.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC zone, NODATA, CNAME */
+ test_rr_lookup(bus, ".wild.nsec.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* wildcard, NSEC3 zone, CNAME */
+ test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_A, NULL);
+ test_hostname_lookup(bus, ".wild.0skar.cz", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, ".wild.0skar.cz", AF_INET, NULL);
+
+ /* wildcard, NSEC3 zone, NODATA, CNAME */
+ test_rr_lookup(bus, ".wild.0skar.cz", DNS_TYPE_RP, BUS_ERROR_NO_SUCH_RR);
+
+ /* NODATA due to empty non-terminal in NSEC domain */
+ test_rr_lookup(bus, "herndon.nasa.gov", DNS_TYPE_A, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_UNSPEC, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET, BUS_ERROR_NO_SUCH_RR);
+ test_hostname_lookup(bus, "herndon.nasa.gov", AF_INET6, BUS_ERROR_NO_SUCH_RR);
+
+ /* NXDOMAIN in NSEC root zone: */
+ test_rr_lookup(bus, "jasdhjas.kjkfgjhfjg", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "jasdhjas.kjkfgjhfjg", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* NXDOMAIN in NSEC3 .com zone: */
+ test_rr_lookup(bus, "kjkfgjhfjgsdfdsfd.com", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, "kjkfgjhfjgsdfdsfd.com", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+
+ /* Unsigned A */
+ test_rr_lookup(bus, "poettering.de", DNS_TYPE_A, NULL);
+ test_rr_lookup(bus, "poettering.de", DNS_TYPE_AAAA, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_INET, NULL);
+ test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL);
+
+#if HAVE_LIBIDN
+ /* Unsigned A with IDNA conversion necessary */
+ test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL);
+ test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL);
+ test_hostname_lookup(bus, "pöttering.de", AF_INET6, NULL);
+#endif
+
+ /* DNAME, pointing to NXDOMAIN */
+ test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_A, _BUS_ERROR_DNS "NXDOMAIN");
+ test_rr_lookup(bus, ".ireallyhpoethisdoesnexist.xn--kprw13d.", DNS_TYPE_RP, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_UNSPEC, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET, _BUS_ERROR_DNS "NXDOMAIN");
+ test_hostname_lookup(bus, ".ireallyhpoethisdoesntexist.xn--kprw13d.", AF_INET6, _BUS_ERROR_DNS "NXDOMAIN");
+
+ return 0;
+}
diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c
index 0c9efde1fe..45fe1997e2 100644
--- a/src/resolve/test-dnssec.c
+++ b/src/resolve/test-dnssec.c
@@ -270,8 +270,8 @@ static void test_dnssec_verify_dns_key(void) {
log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
- assert_se(dnssec_verify_dnskey(dnskey, ds1, false) > 0);
- assert_se(dnssec_verify_dnskey(dnskey, ds2, false) > 0);
+ assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0);
+ assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0);
}
static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) {
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index bc12f89f02..8de1445a96 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -71,7 +71,7 @@ static int lookup_key(const char *keyname, key_serial_t *ret) {
serial = request_key("user", keyname, NULL, 0);
if (serial == -1)
- return -errno;
+ return negative_errno();
*ret = serial;
return 0;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 5c6dc34700..b9a8ee4074 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -2041,13 +2041,21 @@ static const struct {
{ "start-limit", "start of the service was attempted too often" }
};
-static void log_job_error_with_service_result(const char* service, const char *result) {
- _cleanup_free_ char *service_shell_quoted = NULL;
+static void log_job_error_with_service_result(const char* service, const char *result, const char *extra_args) {
+ _cleanup_free_ char *service_shell_quoted = NULL, *systemctl_extra_args = NULL;
assert(service);
service_shell_quoted = shell_maybe_quote(service);
+ systemctl_extra_args = strjoin("systemctl ", extra_args, " ", NULL);
+ if (!systemctl_extra_args) {
+ log_oom();
+ return;
+ }
+
+ systemctl_extra_args = strstrip(systemctl_extra_args);
+
if (!isempty(result)) {
unsigned i;
@@ -2056,27 +2064,30 @@ static void log_job_error_with_service_result(const char* service, const char *r
break;
if (i < ELEMENTSOF(explanations)) {
- log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ log_error("Job for %s failed because %s. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
service,
explanations[i].explanation,
+ systemctl_extra_args,
strna(service_shell_quoted));
goto finish;
}
}
- log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
+ log_error("Job for %s failed. See \"%s status %s\" and \"journalctl -xe\" for details.\n",
service,
+ systemctl_extra_args,
strna(service_shell_quoted));
finish:
/* For some results maybe additional explanation is required */
if (streq_ptr(result, "start-limit"))
- log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
+ log_info("To force a start use \"%1$s reset-failed %2$s\" followed by \"%1$s start %2$s\" again.",
+ systemctl_extra_args,
strna(service_shell_quoted));
}
-static int check_wait_response(BusWaitForJobs *d, bool quiet) {
+static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *extra_args) {
int r = 0;
assert(d->result);
@@ -2103,7 +2114,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
if (q < 0)
log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name);
- log_job_error_with_service_result(d->name, result);
+ log_job_error_with_service_result(d->name, result, extra_args);
} else
log_error("Job failed. See \"journalctl -xe\" for details.");
}
@@ -2127,7 +2138,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) {
return r;
}
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args) {
int r = 0;
assert(d);
@@ -2140,7 +2151,7 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet) {
return log_error_errno(q, "Failed to wait for response: %m");
if (d->result) {
- q = check_wait_response(d, quiet);
+ q = check_wait_response(d, quiet, extra_args);
/* Return the first error as it is most likely to be
* meaningful. */
if (q < 0 && r == 0)
@@ -2175,7 +2186,7 @@ int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
if (r < 0)
return log_oom();
- return bus_wait_for_jobs(d, quiet);
+ return bus_wait_for_jobs(d, quiet, NULL);
}
int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) {
diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h
index a5e3b6a0b5..18fc827754 100644
--- a/src/shared/bus-util.h
+++ b/src/shared/bus-util.h
@@ -182,7 +182,7 @@ typedef struct BusWaitForJobs BusWaitForJobs;
int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
void bus_wait_for_jobs_free(BusWaitForJobs *d);
int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet);
+int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args);
int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet);
DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 59475115ba..7ef4ad3cf8 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -263,7 +263,6 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
*(q++) = '0' + (char) ((uint8_t) *p % 10);
sz -= 4;
-
}
p++;
@@ -406,15 +405,20 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
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 = a;
+ const char *p;
bool first = true;
int r;
- assert(a);
+ if (a)
+ p = a;
+ else if (b) {
+ p = b;
+ b = NULL;
+ } else
+ goto finish;
for (;;) {
char label[DNS_LABEL_MAX];
- int k;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
@@ -433,12 +437,6 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
break;
}
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
-
if (_ret) {
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
@@ -465,12 +463,21 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
n += r;
}
+finish:
if (n > DNS_HOSTNAME_MAX)
return -EINVAL;
if (_ret) {
- if (!GREEDY_REALLOC(ret, allocated, n + 1))
- return -ENOMEM;
+ if (n == 0) {
+ /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
+ if (!GREEDY_REALLOC(ret, allocated, 2))
+ return -ENOMEM;
+
+ ret[n++] = '.';
+ } else {
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
+ }
ret[n] = 0;
*_ret = ret;
@@ -488,7 +495,6 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
for (;;) {
char label[DNS_LABEL_MAX+1];
- int k;
r = dns_label_unescape(&p, label, sizeof(label));
if (r < 0)
@@ -496,12 +502,6 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
if (r == 0)
break;
- k = dns_label_undo_idna(label, r, label, sizeof(label));
- if (k < 0)
- break;
- if (k > 0)
- r = k;
-
ascii_strlower_n(label, r);
siphash24_compress(label, r, state);
siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
@@ -513,7 +513,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
int dns_name_compare_func(const void *a, const void *b) {
const char *x, *y;
- int r, q, k, w;
+ int r, q;
assert(a);
assert(b);
@@ -522,7 +522,7 @@ int dns_name_compare_func(const void *a, const void *b) {
y = (const char *) b + strlen(b);
for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
if (x == NULL && y == NULL)
return 0;
@@ -532,17 +532,7 @@ int dns_name_compare_func(const void *a, const void *b) {
if (r < 0 || q < 0)
return r - q;
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (k < 0 || w < 0)
- return k - w;
- if (k > 0)
- r = k;
- if (w > 0)
- q = w;
-
- la[r] = lb[q] = 0;
- r = strcasecmp(la, lb);
+ r = ascii_strcasecmp_nn(la, r, lb, q);
if (r != 0)
return r;
}
@@ -554,53 +544,35 @@ const struct hash_ops dns_name_hash_ops = {
};
int dns_name_equal(const char *x, const char *y) {
- int r, q, k, w;
+ int r, q;
assert(x);
assert(y);
for (;;) {
- char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
-
- if (*x == 0 && *y == 0)
- return true;
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
r = dns_label_unescape(&x, la, sizeof(la));
if (r < 0)
return r;
- if (r > 0) {
- k = dns_label_undo_idna(la, r, la, sizeof(la));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
- }
q = dns_label_unescape(&y, lb, sizeof(lb));
if (q < 0)
return q;
- if (q > 0) {
- w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
- }
- /* If one name had fewer labels than the other, this
- * will show up as empty label here, which the
- * strcasecmp() below will properly consider different
- * from a non-empty label. */
+ if (r != q)
+ return false;
+ if (r == 0)
+ return true;
- la[r] = lb[q] = 0;
- if (strcasecmp(la, lb) != 0)
+ if (ascii_strcasecmp_n(la, lb, r) != 0)
return false;
}
}
int dns_name_endswith(const char *name, const char *suffix) {
const char *n, *s, *saved_n = NULL;
- int r, q, k, w;
+ int r, q;
assert(name);
assert(suffix);
@@ -609,18 +581,11 @@ int dns_name_endswith(const char *name, const char *suffix) {
s = suffix;
for (;;) {
- char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+ char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
r = dns_label_unescape(&n, ln, sizeof(ln));
if (r < 0)
return r;
- if (r > 0) {
- k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
- }
if (!saved_n)
saved_n = n;
@@ -628,22 +593,13 @@ int dns_name_endswith(const char *name, const char *suffix) {
q = dns_label_unescape(&s, ls, sizeof(ls));
if (q < 0)
return q;
- if (q > 0) {
- w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
- }
if (r == 0 && q == 0)
return true;
if (r == 0 && saved_n == n)
return false;
- ln[r] = ls[q] = 0;
-
- if (r != q || strcasecmp(ln, ls)) {
+ if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
/* Not the same, let's jump back, and try with the next label again */
s = suffix;
@@ -653,9 +609,39 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
+int dns_name_startswith(const char *name, const char *prefix) {
+ const char *n, *p;
+ int r, q;
+
+ assert(name);
+ assert(prefix);
+
+ n = name;
+ p = prefix;
+
+ for (;;) {
+ char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+
+ r = dns_label_unescape(&p, lp, sizeof(lp));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return true;
+
+ q = dns_label_unescape(&n, ln, sizeof(ln));
+ if (q < 0)
+ return q;
+
+ if (r != q)
+ return false;
+ if (ascii_strcasecmp_n(ln, lp, r) != 0)
+ return false;
+ }
+}
+
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
- int r, q, k, w;
+ int r, q;
assert(name);
assert(old_suffix);
@@ -666,7 +652,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
s = old_suffix;
for (;;) {
- char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
+ char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
if (!saved_before)
saved_before = n;
@@ -674,13 +660,6 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
r = dns_label_unescape(&n, ln, sizeof(ln));
if (r < 0)
return r;
- if (r > 0) {
- k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
- if (k < 0)
- return k;
- if (k > 0)
- r = k;
- }
if (!saved_after)
saved_after = n;
@@ -688,13 +667,6 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
q = dns_label_unescape(&s, ls, sizeof(ls));
if (q < 0)
return q;
- if (q > 0) {
- w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
- if (w < 0)
- return w;
- if (w > 0)
- q = w;
- }
if (r == 0 && q == 0)
break;
@@ -703,9 +675,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
return 0;
}
- ln[r] = ls[q] = 0;
-
- if (r != q || strcasecmp(ln, ls)) {
+ if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
/* Not the same, let's jump back, and try with the next label again */
s = old_suffix;
@@ -873,12 +843,11 @@ bool dns_name_is_root(const char *name) {
}
bool dns_name_is_single_label(const char *name) {
- char label[DNS_LABEL_MAX+1];
int r;
assert(name);
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_name_parent(&name);
if (r <= 0)
return false;
@@ -1099,17 +1068,15 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
if (x >= 3 && srv_type_label_is_valid(c, cn)) {
if (dns_service_name_label_is_valid(a, an)) {
-
/* OK, got <name> . <type> . <type2> . <domain> */
name = strndup(a, an);
if (!name)
return -ENOMEM;
- type = new(char, bn+1+cn+1);
+ type = strjoin(b, ".", c, NULL);
if (!type)
return -ENOMEM;
- strcpy(stpcpy(stpcpy(type, b), "."), c);
d = p;
goto finish;
@@ -1121,10 +1088,9 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
name = NULL;
- type = new(char, an+1+bn+1);
+ type = strjoin(a, ".", b, NULL);
if (!type)
return -ENOMEM;
- strcpy(stpcpy(stpcpy(type, a), "."), b);
d = q;
goto finish;
@@ -1158,22 +1124,20 @@ finish:
return 0;
}
-int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
- const char* labels[DNS_N_LABELS_MAX+1];
- unsigned n = 0;
+static int dns_name_build_suffix_table(const char *name, const char*table[]) {
const char *p;
+ unsigned n = 0;
int r;
assert(name);
- assert(ret);
+ assert(table);
p = name;
for (;;) {
if (n > DNS_N_LABELS_MAX)
return -EINVAL;
- labels[n] = p;
-
+ table[n] = p;
r = dns_name_parent(&p);
if (r < 0)
return r;
@@ -1183,13 +1147,47 @@ int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
n++;
}
- if (n < n_labels)
+ return (int) n;
+}
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
+ const char* labels[DNS_N_LABELS_MAX+1];
+ int n;
+
+ assert(name);
+ assert(ret);
+
+ n = dns_name_build_suffix_table(name, labels);
+ if (n < 0)
+ return n;
+
+ if ((unsigned) n < n_labels)
return -EINVAL;
*ret = labels[n - n_labels];
return (int) (n - n_labels);
}
+int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
+ int r;
+
+ assert(a);
+ assert(ret);
+
+ for (; n_labels > 0; n_labels --) {
+ r = dns_name_parent(&a);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *ret = "";
+ return 0;
+ }
+ }
+
+ *ret = a;
+ return 1;
+}
+
int dns_name_count_labels(const char *name) {
unsigned n = 0;
const char *p;
@@ -1220,14 +1218,107 @@ int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
assert(a);
assert(b);
- while (n_labels > 0) {
+ r = dns_name_skip(a, n_labels, &a);
+ if (r <= 0)
+ return r;
- r = dns_name_parent(&a);
- if (r <= 0)
+ return dns_name_equal(a, b);
+}
+
+int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
+ const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
+ int n = 0, m = 0, k = 0, r, q;
+
+ assert(a);
+ assert(b);
+ assert(ret);
+
+ /* Determines the common suffix of domain names a and b */
+
+ n = dns_name_build_suffix_table(a, a_labels);
+ if (n < 0)
+ return n;
+
+ m = dns_name_build_suffix_table(b, b_labels);
+ if (m < 0)
+ return m;
+
+ for (;;) {
+ char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ const char *x, *y;
+
+ if (k >= n || k >= m) {
+ *ret = a_labels[n - k];
+ return 0;
+ }
+
+ x = a_labels[n - 1 - k];
+ r = dns_label_unescape(&x, la, sizeof(la));
+ if (r < 0)
return r;
- n_labels --;
+ y = b_labels[m - 1 - k];
+ q = dns_label_unescape(&y, lb, sizeof(lb));
+ if (q < 0)
+ return q;
+
+ if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
+ *ret = a_labels[n - k];
+ return 0;
+ }
+
+ k++;
}
+}
- return dns_name_equal(a, b);
+int dns_name_apply_idna(const char *name, char **ret) {
+ _cleanup_free_ char *buf = NULL;
+ size_t n = 0, allocated = 0;
+ bool first = true;
+ int r, q;
+
+ assert(name);
+ assert(ret);
+
+ for (;;) {
+ char label[DNS_LABEL_MAX];
+
+ r = dns_label_unescape(&name, label, sizeof(label));
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ q = dns_label_apply_idna(label, r, label, sizeof(label));
+ if (q < 0)
+ return q;
+ if (q > 0)
+ r = q;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
+
+ r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ if (first)
+ first = false;
+ else
+ buf[n++] = '.';
+
+ n +=r;
+ }
+
+ if (n > DNS_HOSTNAME_MAX)
+ return -EINVAL;
+
+ if (!GREEDY_REALLOC(buf, allocated, n + 1))
+ return -ENOMEM;
+
+ buf[n] = 0;
+ *ret = buf;
+ buf = NULL;
+
+ return (int) n;
}
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index dd8ae3ac98..40c9ee5f27 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -83,6 +83,7 @@ extern const struct hash_ops dns_name_hash_ops;
int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
+int dns_name_startswith(const char *name, const char *prefix);
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
@@ -103,4 +104,9 @@ int dns_service_split(const char *joined, char **name, char **type, char **domai
int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
int dns_name_count_labels(const char *name);
+int dns_name_skip(const char *a, unsigned n_labels, const char **ret);
int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);
+
+int dns_name_common_suffix(const char *a, const char *b, const char **ret);
+
+int dns_name_apply_idna(const char *name, char **ret);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index 692e8b8338..073a8396c5 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -156,7 +156,7 @@ static int iterate_dir(
errno = 0;
de = readdir(d);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return log_error_errno(errno, "Failed to read directory %s: %m", path);
if (!de)
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index b1bbbdaadd..bf0739e5fa 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -35,6 +35,7 @@
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "switch-root.h"
#include "user-util.h"
@@ -77,7 +78,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
char new_mount[PATH_MAX];
struct stat sb;
- snprintf(new_mount, sizeof(new_mount), "%s%s", new_root, i);
+ xsprintf(new_mount, "%s%s", new_root, i);
mkdir_p_label(new_mount, 0755);
diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c
index ba82adadb4..600f772e19 100644
--- a/src/socket-proxy/socket-proxyd.c
+++ b/src/socket-proxy/socket-proxyd.c
@@ -505,7 +505,7 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat
if (errno != -EAGAIN)
log_warning_errno(errno, "Failed to accept() socket: %m");
} else {
- getpeername_pretty(nfd, &peer);
+ getpeername_pretty(nfd, true, &peer);
log_debug("New connection from %s", strna(peer));
r = add_connection_socket(context, nfd);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 1448d974bd..94c99c4d91 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -247,7 +247,7 @@ static OutputFlags get_output_flags(void) {
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
- on_tty() * OUTPUT_COLOR |
+ colors_enabled() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF;
}
@@ -1979,7 +1979,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha
for (i = 0; i < n_changes; i++) {
if (changes[i].type == UNIT_FILE_SYMLINK)
- log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source);
+ log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
else
log_info("Removed symlink %s.", changes[i].path);
}
@@ -2325,42 +2325,12 @@ static int unit_find_paths(
if (!install_client_side() && !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *unit_load_error = NULL;
_cleanup_free_ char *unit = NULL;
- char *unit_load_error_name, *unit_load_error_message;
unit = unit_dbus_path_from_name(unit_name);
if (!unit)
return log_oom();
- if (need_daemon_reload(bus, unit_name) > 0)
- warn_unit_file_changed(unit_name);
-
- r = sd_bus_get_property(
- bus,
- "org.freedesktop.systemd1",
- unit,
- "org.freedesktop.systemd1.Unit",
- "LoadError",
- &error,
- &unit_load_error,
- "(ss)");
- if (r < 0)
- return log_error_errno(r, "Failed to get LoadError: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(
- unit_load_error,
- "(ss)",
- &unit_load_error_name,
- &unit_load_error_message);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!isempty(unit_load_error_name)) {
- log_error("Unit %s is not loaded: %s", unit_name, unit_load_error_message);
- return 0;
- }
-
r = sd_bus_get_property_string(
bus,
"org.freedesktop.systemd1",
@@ -2631,7 +2601,13 @@ static int start_unit_one(
verb = method_to_verb(method);
- return log_error_errno(r, "Failed to %s %s: %s", verb, name, bus_error_message(error, r));
+ log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r));
+
+ if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) &&
+ !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED))
+ log_error("See system logs and 'systemctl status %s' for details.", name);
+
+ return r;
}
r = sd_bus_message_read(reply, "o", &path);
@@ -2794,7 +2770,7 @@ static int start_unit(int argc, char *argv[], void *userdata) {
if (!arg_no_block) {
int q;
- q = bus_wait_for_jobs(w, arg_quiet);
+ q = bus_wait_for_jobs(w, arg_quiet, arg_scope != UNIT_FILE_SYSTEM ? "--user" : NULL);
if (q < 0)
return q;
@@ -3177,6 +3153,7 @@ static int check_unit_generic(int code, const char *good_states, char **args) {
sd_bus *bus;
char **name;
int r;
+ bool found = false;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
@@ -3192,11 +3169,13 @@ static int check_unit_generic(int code, const char *good_states, char **args) {
state = check_one_unit(bus, *name, good_states, arg_quiet);
if (state < 0)
return state;
- if (state == 0)
- r = code;
+ if (state > 0)
+ found = true;
}
- return r;
+ /* use the given return code for the case that we won't find
+ * any unit which matches the list */
+ return found ? 0 : code;
}
static int check_unit_active(int argc, char *argv[], void *userdata) {
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 4deaf52e68..bbf220e689 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -42,6 +42,48 @@ enum {
SD_DHCP_CLIENT_EVENT_RENEW = 4,
};
+enum {
+ SD_DHCP_OPTION_PAD = 0,
+ SD_DHCP_OPTION_SUBNET_MASK = 1,
+ SD_DHCP_OPTION_TIME_OFFSET = 2,
+ SD_DHCP_OPTION_ROUTER = 3,
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
+ SD_DHCP_OPTION_HOST_NAME = 12,
+ SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
+ SD_DHCP_OPTION_DOMAIN_NAME = 15,
+ SD_DHCP_OPTION_ROOT_PATH = 17,
+ SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
+ SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
+ SD_DHCP_OPTION_POLICY_FILTER = 21,
+ SD_DHCP_OPTION_INTERFACE_MDR = 22,
+ SD_DHCP_OPTION_INTERFACE_TTL = 23,
+ SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
+ SD_DHCP_OPTION_INTERFACE_MTU = 26,
+ SD_DHCP_OPTION_BROADCAST = 28,
+ SD_DHCP_OPTION_STATIC_ROUTE = 33,
+ SD_DHCP_OPTION_NTP_SERVER = 42,
+ SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
+ SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
+ SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
+ SD_DHCP_OPTION_OVERLOAD = 52,
+ SD_DHCP_OPTION_MESSAGE_TYPE = 53,
+ SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
+ SD_DHCP_OPTION_ERROR_MESSAGE = 56,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
+ SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
+ SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
+ SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
+ SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
+ SD_DHCP_OPTION_FQDN = 81,
+ SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
+ SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
+ SD_DHCP_OPTION_PRIVATE_BASE = 224,
+ SD_DHCP_OPTION_PRIVATE_LAST = 254,
+ SD_DHCP_OPTION_END = 255,
+};
+
typedef struct sd_dhcp_client sd_dhcp_client;
typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event,
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index cb5c2cf173..a0d24c211c 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -33,7 +33,7 @@
_SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp_lease sd_dhcp_lease;
-struct sd_dhcp_route;
+typedef struct sd_dhcp_route sd_dhcp_route;
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
@@ -53,11 +53,15 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes);
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
+int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
+int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 12cc763eb7..fa90f7a670 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -41,6 +41,41 @@ enum {
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
+enum {
+ SD_DHCP6_OPTION_CLIENTID = 1,
+ SD_DHCP6_OPTION_SERVERID = 2,
+ SD_DHCP6_OPTION_IA_NA = 3,
+ SD_DHCP6_OPTION_IA_TA = 4,
+ SD_DHCP6_OPTION_IAADDR = 5,
+ SD_DHCP6_OPTION_ORO = 6,
+ SD_DHCP6_OPTION_PREFERENCE = 7,
+ SD_DHCP6_OPTION_ELAPSED_TIME = 8,
+ SD_DHCP6_OPTION_RELAY_MSG = 9,
+ /* option code 10 is unassigned */
+ SD_DHCP6_OPTION_AUTH = 11,
+ SD_DHCP6_OPTION_UNICAST = 12,
+ SD_DHCP6_OPTION_STATUS_CODE = 13,
+ SD_DHCP6_OPTION_RAPID_COMMIT = 14,
+ SD_DHCP6_OPTION_USER_CLASS = 15,
+ SD_DHCP6_OPTION_VENDOR_CLASS = 16,
+ SD_DHCP6_OPTION_VENDOR_OPTS = 17,
+ SD_DHCP6_OPTION_INTERFACE_ID = 18,
+ SD_DHCP6_OPTION_RECONF_MSG = 19,
+ SD_DHCP6_OPTION_RECONF_ACCEPT = 20,
+
+ SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
+ SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
+
+ SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
+
+ /* option code 35 is unassigned */
+
+ SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
+
+ /* option codes 89-142 are unassigned */
+ /* option codes 144-65535 are unassigned */
+};
+
typedef struct sd_dhcp6_client sd_dhcp6_client;
typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event,
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 1183df6105..814263546b 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -88,6 +88,7 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 653c61a162..ff0d2b191e 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -64,8 +64,11 @@ int sd_network_get_dns(char ***dns);
* representations of IP addresses */
int sd_network_get_ntp(char ***ntp);
-/* Get the search/routing domains for all links. */
-int sd_network_get_domains(char ***domains);
+/* Get the search domains for all links. */
+int sd_network_get_search_domains(char ***domains);
+
+/* Get the search domains for all links. */
+int sd_network_get_route_domains(char ***domains);
/* Get setup state from ifindex.
* Possible states:
@@ -134,8 +137,11 @@ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta);
int sd_network_link_get_lldp(int ifindex, char **lldp);
-/* Get the DNS domain names for a given link. */
-int sd_network_link_get_domains(int ifindex, char ***domains);
+/* Get the search DNS domain names for a given link. */
+int sd_network_link_get_search_domains(int ifindex, char ***domains);
+
+/* Get the route DNS domain names for a given link. */
+int sd_network_link_get_route_domains(int ifindex, char ***domains);
/* Get the CARRIERS to which current link is bound to. */
int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers);
@@ -146,10 +152,6 @@ int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers);
/* Get the timezone that was learnt on a specific link. */
int sd_network_link_get_timezone(int ifindex, char **timezone);
-/* Returns whether or not domains that don't match any link should be resolved
- * on this link. 1 for yes, 0 for no and negative value for error */
-int sd_network_link_get_wildcard_domain(int ifindex);
-
/* Monitor object */
typedef struct sd_network_monitor sd_network_monitor;
diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h
index 241b51084d..eb4548a2dc 100644
--- a/src/systemd/sd-resolve.h
+++ b/src/systemd/sd-resolve.h
@@ -44,9 +44,9 @@ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, co
typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata);
enum {
- SD_RESOLVE_GET_HOST = 1ULL,
- SD_RESOLVE_GET_SERVICE = 2ULL,
- SD_RESOLVE_GET_BOTH = 3ULL
+ SD_RESOLVE_GET_HOST = UINT64_C(1),
+ SD_RESOLVE_GET_SERVICE = UINT64_C(2),
+ SD_RESOLVE_GET_BOTH = UINT64_C(3),
};
int sd_resolve_default(sd_resolve **ret);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 675f94906b..b1dd7e1913 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -280,7 +280,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
errno = 0;
if (putgrent(&t, group) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 1;
}
@@ -288,7 +288,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) {
errno = 0;
if (putgrent(gr, group) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -330,7 +330,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
errno = 0;
if (putsgent(&t, gshadow) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 1;
}
@@ -338,7 +338,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
errno = 0;
if (putsgent(sg, gshadow) != 0)
- return errno ? -errno : -EIO;
+ return errno > 0 ? -errno : -EIO;
return 0;
}
@@ -410,11 +410,13 @@ static int write_files(void) {
i = hashmap_get(groups, gr->gr_name);
if (i && i->todo_group) {
+ log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
r = -EEXIST;
goto finish;
}
if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
+ log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
r = -EEXIST;
goto finish;
}
@@ -482,6 +484,7 @@ static int write_files(void) {
i = hashmap_get(groups, sg->sg_namp);
if (i && i->todo_group) {
+ log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
r = -EEXIST;
goto finish;
}
@@ -548,11 +551,13 @@ static int write_files(void) {
i = hashmap_get(users, pw->pw_name);
if (i && i->todo_user) {
+ log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
r = -EEXIST;
goto finish;
}
if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
+ log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
r = -EEXIST;
goto finish;
}
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 5075548507..d48d5abbe3 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -161,7 +161,6 @@ static int add_alias(const char *service, const char *alias) {
}
static int generate_unit_file(SysvStub *s) {
- _cleanup_free_ char *before = NULL, *after = NULL, *wants = NULL, *conflicts = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *unit;
char **p;
@@ -174,14 +173,6 @@ static int generate_unit_file(SysvStub *s) {
unit = strjoina(arg_dest, "/", s->name);
- before = strv_join(s->before, " ");
- after = strv_join(s->after, " ");
- wants = strv_join(s->wants, " ");
- conflicts = strv_join(s->conflicts, " ");
-
- if (!before || !after || !wants || !conflicts)
- return log_oom();
-
/* We might already have a symlink with the same name from a Provides:,
* or from backup files like /etc/init.d/foo.bak. Real scripts always win,
* so remove an existing link */
@@ -204,14 +195,14 @@ static int generate_unit_file(SysvStub *s) {
if (s->description)
fprintf(f, "Description=%s\n", s->description);
- if (!isempty(before))
- fprintf(f, "Before=%s\n", before);
- if (!isempty(after))
- fprintf(f, "After=%s\n", after);
- if (!isempty(wants))
- fprintf(f, "Wants=%s\n", wants);
- if (!isempty(conflicts))
- fprintf(f, "Conflicts=%s\n", conflicts);
+ STRV_FOREACH(p, s->before)
+ fprintf(f, "Before=%s\n", *p);
+ STRV_FOREACH(p, s->after)
+ fprintf(f, "After=%s\n", *p);
+ STRV_FOREACH(p, s->wants)
+ fprintf(f, "Wants=%s\n", *p);
+ STRV_FOREACH(p, s->conflicts)
+ fprintf(f, "Conflicts=%s\n", *p);
fprintf(f,
"\n[Service]\n"
diff --git a/src/test/test-capability.c b/src/test/test-capability.c
index fc8d3ffe0d..629bb63c81 100644
--- a/src/test/test-capability.c
+++ b/src/test/test-capability.c
@@ -20,6 +20,7 @@
#include <netinet/in.h>
#include <pwd.h>
#include <sys/capability.h>
+#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -66,8 +67,9 @@ static void show_capabilities(void) {
cap_free(text);
}
-static int setup_tests(void) {
+static int setup_tests(bool *run_ambient) {
struct passwd *nobody;
+ int r;
nobody = getpwnam("nobody");
if (!nobody) {
@@ -77,6 +79,18 @@ static int setup_tests(void) {
test_uid = nobody->pw_uid;
test_gid = nobody->pw_gid;
+ *run_ambient = false;
+
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+
+ /* There's support for PR_CAP_AMBIENT if the prctl() call
+ * succeeded or error code was something else than EINVAL. The
+ * EINVAL check should be good enough to rule out false
+ * positives. */
+
+ if (r >= 0 || errno != EINVAL)
+ *run_ambient = true;
+
return 0;
}
@@ -140,8 +154,53 @@ static void test_have_effective_cap(void) {
assert_se(!have_effective_cap(CAP_CHOWN));
}
+static void test_update_inherited_set(void) {
+ cap_t caps;
+ uint64_t set = 0;
+ cap_flag_value_t fv;
+
+ caps = cap_get_proc();
+ assert_se(caps);
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_CLEAR);
+
+ set = (UINT64_C(1) << CAP_CHOWN);
+
+ assert_se(!capability_update_inherited_set(caps, set));
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_SET);
+
+ cap_free(caps);
+}
+
+static void test_set_ambient_caps(void) {
+ cap_t caps;
+ uint64_t set = 0;
+ cap_flag_value_t fv;
+
+ caps = cap_get_proc();
+ assert_se(caps);
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_CLEAR);
+ cap_free(caps);
+
+ assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
+
+ set = (UINT64_C(1) << CAP_CHOWN);
+
+ assert_se(!capability_ambient_set_apply(set, true));
+
+ caps = cap_get_proc();
+ assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+ assert(fv == CAP_SET);
+ cap_free(caps);
+
+ assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
+}
+
int main(int argc, char *argv[]) {
int r;
+ bool run_ambient;
log_parse_environment();
log_open();
@@ -149,14 +208,19 @@ int main(int argc, char *argv[]) {
if (getuid() != 0)
return EXIT_TEST_SKIP;
- r = setup_tests();
+ r = setup_tests(&run_ambient);
if (r < 0)
return -r;
show_capabilities();
test_drop_privileges();
+ test_update_inherited_set();
+
fork_test(test_have_effective_cap);
+ if (run_ambient)
+ fork_test(test_set_ambient_caps);
+
return 0;
}
diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c
index 2746013522..a33519b9da 100644
--- a/src/test/test-cgroup-mask.c
+++ b/src/test/test-cgroup-mask.c
@@ -40,6 +40,7 @@ static int test_cgroup_mask(void) {
puts("manager_new: Permission denied. Skipping test.");
return EXIT_TEST_SKIP;
}
+ assert_se(r >= 0);
/* Turn off all kinds of default accouning, so that we can
* verify the masks resulting of our configuration and nothing
diff --git a/src/test/test-date.c b/src/test/test-date.c
index c6d8bf82ea..851d384117 100644
--- a/src/test/test-date.c
+++ b/src/test/test-date.c
@@ -27,14 +27,16 @@
static void test_should_pass(const char *p) {
usec_t t, q;
- char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX];
+ char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *sp;
assert_se(parse_timestamp(p, &t) >= 0);
format_timestamp_us(buf, sizeof(buf), t);
log_info("%s", buf);
/* Chop off timezone */
- *strrchr(buf, ' ') = 0;
+ sp = strrchr(buf, ' ');
+ assert_se(sp);
+ *sp = 0;
assert_se(parse_timestamp(buf, &q) >= 0);
assert_se(q == t);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 6c3c49908f..3efc61ad0a 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -186,7 +186,7 @@ static void test_dns_name_normalize_one(const char *what, const char *expect, in
}
static void test_dns_name_normalize(void) {
- test_dns_name_normalize_one("", "", 0);
+ test_dns_name_normalize_one("", ".", 0);
test_dns_name_normalize_one("f", "f", 0);
test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
@@ -194,7 +194,7 @@ static void test_dns_name_normalize(void) {
test_dns_name_normalize_one("..", NULL, -EINVAL);
test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
test_dns_name_normalize_one("foobar.", "foobar", 0);
- test_dns_name_normalize_one(".", "", 0);
+ test_dns_name_normalize_one(".", ".", 0);
}
static void test_dns_name_equal_one(const char *a, const char *b, int ret) {
@@ -276,6 +276,25 @@ static void test_dns_name_endswith(void) {
test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
}
+static void test_dns_name_startswith_one(const char *a, const char *b, int ret) {
+ assert_se(dns_name_startswith(a, b) == ret);
+}
+
+static void test_dns_name_startswith(void) {
+ test_dns_name_startswith_one("", "", true);
+ test_dns_name_startswith_one("", "xxx", false);
+ test_dns_name_startswith_one("xxx", "", true);
+ test_dns_name_startswith_one("x", "x", true);
+ test_dns_name_startswith_one("x", "y", false);
+ test_dns_name_startswith_one("x.y", "x.y", true);
+ test_dns_name_startswith_one("x.y", "y.x", false);
+ test_dns_name_startswith_one("x.y", "x", true);
+ test_dns_name_startswith_one("x.y", "X", true);
+ test_dns_name_startswith_one("x.y", "y", false);
+ test_dns_name_startswith_one("x.y", "", true);
+ test_dns_name_startswith_one("x.y", "X", true);
+}
+
static void test_dns_name_is_root(void) {
assert_se(dns_name_is_root(""));
assert_se(dns_name_is_root("."));
@@ -321,10 +340,18 @@ static void test_dns_name_concat_one(const char *a, const char *b, int r, const
}
static void test_dns_name_concat(void) {
+ test_dns_name_concat_one("", "", 0, ".");
+ test_dns_name_concat_one(".", "", 0, ".");
+ test_dns_name_concat_one("", ".", 0, ".");
+ test_dns_name_concat_one(".", ".", 0, ".");
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", ".", 0, "foo");
test_dns_name_concat_one("foo.", "bar.", 0, "foo.bar");
+ test_dns_name_concat_one(NULL, NULL, 0, ".");
+ test_dns_name_concat_one(NULL, ".", 0, ".");
+ test_dns_name_concat_one(NULL, "foo", 0, "foo");
}
static void test_dns_name_is_valid_one(const char *s, int ret) {
@@ -410,7 +437,7 @@ static void test_dns_service_join_one(const char *a, const char *b, const char *
assert_se(dns_service_split(t, &x, &y, &z) >= 0);
assert_se(streq_ptr(a, x));
assert_se(streq_ptr(b, y));
- assert_se(streq_ptr(c, z));
+ assert_se(dns_name_equal(c, z) > 0);
}
static void test_dns_service_join(void) {
@@ -441,18 +468,18 @@ static void test_dns_service_split_one(const char *joined, const char *a, const
if (y) {
assert_se(dns_service_join(x, y, z, &t) == 0);
- assert_se(streq_ptr(joined, t));
+ assert_se(dns_name_equal(joined, t) > 0);
} else
- assert_se(!x && streq_ptr(z, joined));
+ assert_se(!x && dns_name_equal(z, joined) > 0);
}
static void test_dns_service_split(void) {
- test_dns_service_split_one("", NULL, NULL, "", 0);
+ test_dns_service_split_one("", NULL, NULL, ".", 0);
test_dns_service_split_one("foo", NULL, NULL, "foo", 0);
test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0);
test_dns_service_split_one("_foo.bar", NULL, NULL, "_foo.bar", 0);
- test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", "", 0);
- test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", "", 0);
+ test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", ".", 0);
+ test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", ".", 0);
test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0);
}
@@ -471,7 +498,7 @@ static void test_dns_name_change_suffix(void) {
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff");
test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff");
- test_dns_name_change_suffix_one("", "", "", 1, "");
+ test_dns_name_change_suffix_one("", "", "", 1, ".");
test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
}
@@ -559,6 +586,46 @@ static void test_dns_name_compare_func(void) {
assert_se(dns_name_compare_func("de.", "heise.de") != 0);
}
+static void test_dns_name_common_suffix_one(const char *a, const char *b, const char *result) {
+ const char *c;
+
+ assert_se(dns_name_common_suffix(a, b, &c) >= 0);
+ assert_se(streq(c, result));
+}
+
+static void test_dns_name_common_suffix(void) {
+ test_dns_name_common_suffix_one("", "", "");
+ test_dns_name_common_suffix_one("foo", "", "");
+ test_dns_name_common_suffix_one("", "foo", "");
+ test_dns_name_common_suffix_one("foo", "bar", "");
+ test_dns_name_common_suffix_one("bar", "foo", "");
+ test_dns_name_common_suffix_one("foo", "foo", "foo");
+ test_dns_name_common_suffix_one("quux.foo", "foo", "foo");
+ test_dns_name_common_suffix_one("foo", "quux.foo", "foo");
+ test_dns_name_common_suffix_one("this.is.a.short.sentence", "this.is.another.short.sentence", "short.sentence");
+ test_dns_name_common_suffix_one("FOO.BAR", "tEST.bAR", "BAR");
+}
+
+static void test_dns_name_apply_idna_one(const char *s, const char *result) {
+#ifdef HAVE_LIBIDN
+ _cleanup_free_ char *buf = NULL;
+ assert_se(dns_name_apply_idna(s, &buf) >= 0);
+ assert_se(dns_name_equal(buf, result) > 0);
+#endif
+}
+
+static void test_dns_name_apply_idna(void) {
+ test_dns_name_apply_idna_one("", "");
+ test_dns_name_apply_idna_one("foo", "foo");
+ test_dns_name_apply_idna_one("foo.", "foo");
+ test_dns_name_apply_idna_one("foo.bar", "foo.bar");
+ test_dns_name_apply_idna_one("foo.bar.", "foo.bar");
+ test_dns_name_apply_idna_one("föö", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.", "xn--f-1gaa");
+ test_dns_name_apply_idna_one("föö.bär", "xn--f-1gaa.xn--br-via");
+ test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via");
+}
+
int main(int argc, char *argv[]) {
test_dns_label_unescape();
@@ -567,6 +634,7 @@ int main(int argc, char *argv[]) {
test_dns_name_normalize();
test_dns_name_equal();
test_dns_name_endswith();
+ test_dns_name_startswith();
test_dns_name_between();
test_dns_name_is_root();
test_dns_name_is_single_label();
@@ -583,6 +651,8 @@ int main(int argc, char *argv[]) {
test_dns_name_count_labels();
test_dns_name_equal_skip();
test_dns_name_compare_func();
+ test_dns_name_common_suffix();
+ test_dns_name_apply_idna();
return 0;
}
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 753afadb0a..92857cb5e2 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -20,6 +20,7 @@
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include "fileio.h"
@@ -224,6 +225,20 @@ static void test_exec_capabilityboundingset(Manager *m) {
test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
}
+static void test_exec_capabilityambientset(Manager *m) {
+ int r;
+
+ /* Check if the kernel has support for ambient capabilities. Run
+ * the tests only if that's the case. Clearing all ambient
+ * capabilities is fine, since we are expecting them to be unset
+ * in the first place for the tests. */
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+ if (r >= 0 || errno != EINVAL) {
+ test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
+ test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
+ }
+}
+
static void test_exec_privatenetwork(Manager *m) {
int r;
@@ -266,6 +281,7 @@ int main(int argc, char *argv[]) {
test_exec_umask,
test_exec_runtimedirectory,
test_exec_capabilityboundingset,
+ test_exec_capabilityambientset,
test_exec_oomscoreadjust,
test_exec_ioschedulingclass,
NULL,
diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c
index 350eaf734d..94d852b3b0 100644
--- a/src/test/test-libudev.c
+++ b/src/test/test-libudev.c
@@ -25,6 +25,7 @@
#include "libudev.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "udev-util.h"
#include "util.h"
@@ -460,7 +461,7 @@ int main(int argc, char *argv[]) {
/* add sys path if needed */
if (!startswith(syspath, "/sys")) {
- snprintf(path, sizeof(path), "/sys/%s", syspath);
+ xsprintf(path, "/sys/%s", syspath);
syspath = path;
}
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
index 25444c794a..12889ce873 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -55,7 +55,53 @@ static void test_string_erase(void) {
assert_se(streq(string_erase(x), "xxxxxxxxx"));
}
+static void test_ascii_strcasecmp_n(void) {
+
+ assert_se(ascii_strcasecmp_n("", "", 0) == 0);
+ assert_se(ascii_strcasecmp_n("", "", 1) == 0);
+ assert_se(ascii_strcasecmp_n("", "a", 1) < 0);
+ assert_se(ascii_strcasecmp_n("", "a", 2) < 0);
+ assert_se(ascii_strcasecmp_n("a", "", 1) > 0);
+ assert_se(ascii_strcasecmp_n("a", "", 2) > 0);
+ assert_se(ascii_strcasecmp_n("a", "a", 1) == 0);
+ assert_se(ascii_strcasecmp_n("a", "a", 2) == 0);
+ assert_se(ascii_strcasecmp_n("a", "b", 1) < 0);
+ assert_se(ascii_strcasecmp_n("a", "b", 2) < 0);
+ assert_se(ascii_strcasecmp_n("b", "a", 1) > 0);
+ assert_se(ascii_strcasecmp_n("b", "a", 2) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxYxxxx", 9) == 0);
+ assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxyxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxyxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxYxxxx", 9) < 0);
+ assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxYxxxx", 9) < 0);
+
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxYxxxx", 9) == 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxxxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxXxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxxxxxx", 9) > 0);
+ assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxXxxxx", 9) > 0);
+}
+
+static void test_ascii_strcasecmp_nn(void) {
+ assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0);
+ assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0);
+ assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0);
+ assert_se(ascii_strcasecmp_nn("", 1, "", 1) == 0);
+
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaAa", 4) == 0);
+ assert_se(ascii_strcasecmp_nn("aaa", 3, "aaAa", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaa", 4, "aaAa", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaA", 3) > 0);
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "AAA", 4) > 0);
+
+ assert_se(ascii_strcasecmp_nn("aaaa", 4, "bbbb", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("aaAA", 4, "BBbb", 4) < 0);
+ assert_se(ascii_strcasecmp_nn("BBbb", 4, "aaaa", 4) > 0);
+}
+
int main(int argc, char *argv[]) {
test_string_erase();
+ test_ascii_strcasecmp_n();
+ test_ascii_strcasecmp_nn();
return 0;
}
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index a8bd722e44..23f26369bd 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -28,6 +28,8 @@
#include "fd-util.h"
#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "log.h"
#include "string-util.h"
#include "util.h"
@@ -35,20 +37,29 @@ int main(int argc, char** argv) {
const char *p = argv[1] ?: "/tmp";
char *pattern = strjoina(p, "/systemd-test-XXXXXX");
_cleanup_close_ int fd, fd2;
- _cleanup_free_ char *cmd, *cmd2;
+ _cleanup_free_ char *cmd, *cmd2, *ans, *ans2;
+
+ log_set_max_level(LOG_DEBUG);
+ log_parse_environment();
fd = open_tmpfile(p, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
- system(cmd);
+ (void) system(cmd);
+ assert_se(readlink_malloc(cmd + 6, &ans) >= 0);
+ log_debug("link1: %s", ans);
+ assert_se(endswith(ans, " (deleted)"));
fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
assert_se(unlink(pattern) == 0);
assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0);
- system(cmd2);
+ (void) system(cmd2);
+ assert_se(readlink_malloc(cmd2 + 6, &ans2) >= 0);
+ log_debug("link2: %s", ans2);
+ assert_se(endswith(ans2, " (deleted)"));
return 0;
}
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 42605140c4..199623e025 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -28,6 +28,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "capability-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
@@ -641,8 +642,8 @@ static uint64_t make_cap(int cap) {
return ((uint64_t) 1ULL << (uint64_t) cap);
}
-static void test_config_parse_bounding_set(void) {
- /* int config_parse_bounding_set(
+static void test_config_parse_capability_set(void) {
+ /* int config_parse_capability_set(
const char *unit,
const char *filename,
unsigned line,
@@ -654,38 +655,38 @@ static void test_config_parse_bounding_set(void) {
void *data,
void *userdata) */
int r;
- uint64_t capability_bounding_set_drop = 0;
+ uint64_t capability_bounding_set = 0;
- r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "CAP_NET_RAW",
- &capability_bounding_set_drop, NULL);
+ &capability_bounding_set, NULL);
assert_se(r >= 0);
- assert_se(capability_bounding_set_drop == ~make_cap(CAP_NET_RAW));
+ assert_se(capability_bounding_set == make_cap(CAP_NET_RAW));
- r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
- &capability_bounding_set_drop, NULL);
+ &capability_bounding_set, NULL);
assert_se(r >= 0);
- assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+ assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
- r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "",
- &capability_bounding_set_drop, NULL);
+ &capability_bounding_set, NULL);
assert_se(r >= 0);
- assert_se(capability_bounding_set_drop == ~((uint64_t) 0ULL));
+ assert_se(capability_bounding_set == UINT64_C(0));
- r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, "~",
- &capability_bounding_set_drop, NULL);
+ &capability_bounding_set, NULL);
assert_se(r >= 0);
- assert_se(capability_bounding_set_drop == (uint64_t) 0ULL);
+ assert_se(cap_test_all(capability_bounding_set));
- capability_bounding_set_drop = 0;
- r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
+ capability_bounding_set = 0;
+ r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
"CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
- &capability_bounding_set_drop, NULL);
+ &capability_bounding_set, NULL);
assert_se(r >= 0);
- assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
+ assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
}
static void test_config_parse_rlimit(void) {
@@ -845,7 +846,7 @@ int main(int argc, char *argv[]) {
r = test_unit_file_get_set();
test_config_parse_exec();
- test_config_parse_bounding_set();
+ test_config_parse_capability_set();
test_config_parse_rlimit();
test_config_parse_pass_environ();
test_load_env_file_1();
diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c
index 8dca538b3b..5627d17de1 100644
--- a/src/timesync/timesyncd-manager.c
+++ b/src/timesync/timesyncd-manager.c
@@ -372,7 +372,8 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
if (r < 0)
return -errno;
- touch("/var/lib/systemd/clock");
+ /* If touch fails, there isn't much we can do. Maybe it'll work next time. */
+ (void) touch("/var/lib/systemd/clock");
m->drift_ppm = tmx.freq / 65536;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index f9a759e223..bb81ff5e3a 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1075,7 +1075,7 @@ static int item_do_children(Item *i, const char *path, action_t action) {
errno = 0;
de = readdir(d);
if (!de) {
- if (errno != 0 && r == 0)
+ if (errno > 0 && r == 0)
r = -errno;
break;
diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c
index b6c95cd452..349585b634 100644
--- a/src/udev/collect/collect.c
+++ b/src/udev/collect/collect.c
@@ -27,6 +27,7 @@
#include "alloc-util.h"
#include "libudev-private.h"
#include "macro.h"
+#include "stdio-util.h"
#include "string-util.h"
#define BUFSIZE 16
@@ -91,7 +92,7 @@ static int prepare(char *dir, char *filename)
if (r < 0 && errno != EEXIST)
return -errno;
- snprintf(buf, sizeof(buf), "%s/%s", dir, filename);
+ xsprintf(buf, "%s/%s", dir, filename);
fd = open(buf,O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
if (fd < 0)
diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c
index 0b1ae706e7..018b4dc596 100644
--- a/src/udev/udev-builtin-blkid.c
+++ b/src/udev/udev-builtin-blkid.c
@@ -124,7 +124,7 @@ static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) {
errno = 0;
pl = blkid_probe_get_partitions(pr);
if (!pl)
- return errno ? -errno : -ENOMEM;
+ return errno > 0 ? -errno : -ENOMEM;
nvals = blkid_partlist_numof_partitions(pl);
for (i = 0; i < nvals; i++) {
diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c
index a7aac78def..691ef5656d 100644
--- a/src/udev/udev-builtin-input_id.c
+++ b/src/udev/udev-builtin-input_id.c
@@ -33,6 +33,7 @@
#include <linux/input.h>
#include "fd-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "udev.h"
#include "util.h"
@@ -66,8 +67,8 @@ static void extract_info(struct udev_device *dev, const char *devpath, bool test
if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0)
return;
- snprintf(width, sizeof(width), "%d", abs_size_mm(&xabsinfo));
- snprintf(height, sizeof(height), "%d", abs_size_mm(&yabsinfo));
+ xsprintf(width, "%d", abs_size_mm(&xabsinfo));
+ xsprintf(height, "%d", abs_size_mm(&yabsinfo));
udev_builtin_add_property(dev, test, "ID_INPUT_WIDTH_MM", width);
udev_builtin_add_property(dev, test, "ID_INPUT_HEIGHT_MM", height);
@@ -93,7 +94,7 @@ static void get_cap_mask(struct udev_device *dev,
if (!v)
v = "";
- snprintf(text, sizeof(text), "%s", v);
+ xsprintf(text, "%s", v);
log_debug("%s raw kernel attribute: %s", attr, text);
memzero(bitmask, bitmask_size);
@@ -115,7 +116,8 @@ static void get_cap_mask(struct udev_device *dev,
if (test) {
/* printf pattern with the right unsigned long number of hex chars */
- snprintf(text, sizeof(text), " bit %%4u: %%0%zulX\n", 2 * sizeof(unsigned long));
+ xsprintf(text, " bit %%4u: %%0%zulX\n",
+ 2 * sizeof(unsigned long));
log_debug("%s decoded bit map:", attr);
val = bitmask_size / sizeof (unsigned long);
/* skip over leading zeros */
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index e549fdbee9..104d5111c5 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -102,9 +102,12 @@
#include "fd-util.h"
#include "fileio.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "udev.h"
+#define ONBOARD_INDEX_MAX (16*1024-1)
+
enum netname_type{
NET_UNDEF,
NET_PCI,
@@ -151,6 +154,13 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
if (idx <= 0)
return -EINVAL;
+ /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
+ * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary
+ * cut-off, which is somewhere beyond the realistic number of physical network interface a system might
+ * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */
+ if (idx > ONBOARD_INDEX_MAX)
+ return -ENOENT;
+
/* kernel provided port index for multiple ports on a single PCI function */
attr = udev_device_get_sysattr_value(dev, "dev_port");
if (attr)
@@ -228,7 +238,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
err = -ENOENT;
goto out;
}
- snprintf(slots, sizeof(slots), "%s/slots", udev_device_get_syspath(pci));
+ xsprintf(slots, "%s/slots", udev_device_get_syspath(pci));
dir = opendir(slots);
if (!dir) {
err = -errno;
@@ -247,7 +257,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
continue;
if (i < 1)
continue;
- snprintf(str, sizeof(str), "%s/%s/address", slots, dent->d_name);
+ xsprintf(str, "%s/%s/address", slots, dent->d_name);
if (read_one_line_file(str, &address) >= 0) {
/* match slot address with device by stripping the function */
if (strneq(address, udev_device_get_sysname(names->pcidev), strlen(address)))
@@ -380,7 +390,7 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) {
return -EINVAL;
/* suppress the common core == 0 */
if (core > 0)
- snprintf(names->bcma_core, sizeof(names->bcma_core), "b%u", core);
+ xsprintf(names->bcma_core, "b%u", core);
names->type = NET_BCMA;
return 0;
@@ -469,9 +479,9 @@ static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test)
/* skip commonly misused 00:00:00 (Xerox) prefix */
if (memcmp(names->mac, "\0\0\0", 3) == 0)
return -EINVAL;
- snprintf(str, sizeof(str), "OUI:%02X%02X%02X%02X%02X%02X",
- names->mac[0], names->mac[1], names->mac[2],
- names->mac[3], names->mac[4], names->mac[5]);
+ xsprintf(str, "OUI:%02X%02X%02X%02X%02X%02X", names->mac[0],
+ names->mac[1], names->mac[2], names->mac[3], names->mac[4],
+ names->mac[5]);
udev_builtin_hwdb_lookup(dev, NULL, str, NULL, test);
return 0;
}
@@ -523,7 +533,7 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
if (err >= 0 && names.mac_valid) {
char str[IFNAMSIZ];
- snprintf(str, sizeof(str), "%sx%02x%02x%02x%02x%02x%02x", prefix,
+ xsprintf(str, "%sx%02x%02x%02x%02x%02x%02x", prefix,
names.mac[0], names.mac[1], names.mac[2],
names.mac[3], names.mac[4], names.mac[5]);
udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 39ae2cc1b1..fd7936c2dc 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -31,6 +31,7 @@
#include "fs-util.h"
#include "selinux-util.h"
#include "smack-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "udev.h"
@@ -348,9 +349,10 @@ void udev_node_add(struct udev_device *dev, bool apply,
return;
/* always add /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
+ xsprintf(filename, "/dev/%s/%u:%u",
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
+ major(udev_device_get_devnum(dev)),
+ minor(udev_device_get_devnum(dev)));
node_symlink(dev, udev_device_get_devnode(dev), filename);
/* create/update symlinks, add symlinks to name index */
@@ -367,8 +369,9 @@ void udev_node_remove(struct udev_device *dev) {
link_update(dev, udev_list_entry_get_name(list_entry), false);
/* remove /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), "/dev/%s/%u:%u",
+ xsprintf(filename, "/dev/%s/%u:%u",
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
- major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
+ major(udev_device_get_devnum(dev)),
+ minor(udev_device_get_devnum(dev)));
unlink(filename);
}
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index 60de703706..c0f4973f93 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -26,6 +26,7 @@
#include <sys/inotify.h>
#include <unistd.h>
+#include "stdio-util.h"
#include "udev.h"
static int inotify_fd = -1;
@@ -105,7 +106,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
return;
}
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
mkdir_parents(filename, 0755);
unlink(filename);
r = symlink(udev_device_get_id_filename(dev), filename);
@@ -129,7 +130,7 @@ void udev_watch_end(struct udev *udev, struct udev_device *dev) {
log_debug("removing watch on '%s'", udev_device_get_devnode(dev));
inotify_rm_watch(inotify_fd, wd);
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
unlink(filename);
udev_device_set_watch_handle(dev, -1);
@@ -143,7 +144,7 @@ struct udev_device *udev_watch_lookup(struct udev *udev, int wd) {
if (inotify_fd < 0 || wd < 0)
return NULL;
- snprintf(filename, sizeof(filename), "/run/udev/watch/%d", wd);
+ xsprintf(filename, "/run/udev/watch/%d", wd);
len = readlink(filename, device, sizeof(device));
if (len <= 0 || (size_t)len == sizeof(device))
return NULL;
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 366e7fbb87..e658d6a079 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -1652,7 +1652,8 @@ exit:
int main(int argc, char *argv[]) {
_cleanup_free_ char *cgroup = NULL;
- int r, fd_ctrl, fd_uevent;
+ int fd_ctrl = -1, fd_uevent = -1;
+ int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
index a5f4529cfd..622fbe9a6d 100644
--- a/src/vconsole/vconsole-setup.c
+++ b/src/vconsole/vconsole-setup.c
@@ -39,6 +39,7 @@
#include "log.h"
#include "process-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "terminal-util.h"
#include "util.h"
@@ -215,11 +216,11 @@ static void font_copy_to_all_vcs(int fd) {
continue;
/* skip non-allocated ttys */
- snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
+ xsprintf(vcname, "/dev/vcs%i", i);
if (access(vcname, F_OK) < 0)
continue;
- snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
+ xsprintf(vcname, "/dev/tty%i", i);
vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
if (vcfd < 0)
continue;