]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #2222 from snakeroot/eventsplat
authorDaniel Mack <github@zonque.org>
Wed, 20 Jan 2016 16:06:56 +0000 (17:06 +0100)
committerDaniel Mack <github@zonque.org>
Wed, 20 Jan 2016 16:06:56 +0000 (17:06 +0100)
hwdb: remove references to udevadm info /dev/input/event*

249 files changed:
.dir-locals.el
.vimrc
Makefile-man.am
Makefile.am
README
TODO
catalog/systemd.hu.catalog [new file with mode: 0644]
coccinelle/xsprintf.cocci [new file with mode: 0644]
configure.ac
hwdb/20-bluetooth-vendor-product.hwdb
hwdb/60-evdev.hwdb
hwdb/60-keyboard.hwdb
hwdb/70-mouse.hwdb
man/busctl.xml
man/dnssec-trust-anchors.d.xml [new file with mode: 0644]
man/journal-remote.conf.xml
man/kernel-command-line.xml
man/machine-id.xml
man/machinectl.xml
man/resolved.conf.xml
man/sd-event.xml
man/sd_bus_creds_get_pid.xml
man/sd_event_add_child.xml
man/sd_event_add_defer.xml
man/sd_event_add_io.xml
man/sd_event_add_signal.xml
man/sd_event_add_time.xml
man/sd_event_exit.xml
man/sd_event_now.xml
man/sd_event_source_set_enabled.xml
man/sd_event_source_set_prepare.xml
man/sd_event_source_set_priority.xml
man/sd_event_wait.xml
man/sd_notify.xml
man/sd_seat_get_active.xml
man/systemctl.xml
man/systemd-journal-gatewayd.service.xml
man/systemd-resolved.service.xml
man/systemd.exec.xml
man/systemd.generator.xml
man/systemd.network.xml
man/systemd.special.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/systemd.xml
man/tmpfiles.d.xml
po/hu.po
po/uk.po
src/analyze/analyze-verify.c
src/backlight/backlight.c
src/basic/bitmap.c
src/basic/btrfs-util.c
src/basic/capability-util.c
src/basic/capability-util.h
src/basic/cgroup-util.c
src/basic/clock-util.c
src/basic/conf-files.c
src/basic/errno-list.c
src/basic/escape.c
src/basic/escape.h
src/basic/extract-word.c
src/basic/fd-util.h
src/basic/fileio.c
src/basic/fs-util.c
src/basic/glob-util.c
src/basic/hash-funcs.c [new file with mode: 0644]
src/basic/hash-funcs.h [new file with mode: 0644]
src/basic/hashmap.c
src/basic/hashmap.h
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/log.c
src/basic/macro.h
src/basic/missing.h
src/basic/parse-util.c
src/basic/path-util.c
src/basic/rm-rf.c
src/basic/signal-util.c
src/basic/siphash24.h
src/basic/string-table.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/terminal-util.c
src/basic/terminal-util.h
src/basic/user-util.c
src/basic/util.c
src/boot/bootctl.c
src/bootchart/svg.c
src/cgtop/cgtop.c
src/core/dbus-execute.c
src/core/dbus-manager.c
src/core/device.c
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/load-dropin.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/machine-id-setup.c
src/core/machine-id-setup.h
src/core/main.c
src/core/manager.c
src/core/mount-setup.c
src/core/smack-setup.c
src/core/socket.c
src/core/transaction.c
src/core/unit.c
src/firstboot/firstboot.c
src/getty-generator/getty-generator.c
src/import/aufs-util.c
src/import/import-common.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote.c
src/journal-remote/journal-remote.conf.in
src/journal-remote/microhttpd-util.h
src/journal/coredump.c
src/journal/journalctl.c
src/journal/journald-native.c
src/journal/journald-server.c
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-lldp.c
src/libsystemd-network/sd-ndisc.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-error.c
src/libsystemd/sd-bus/bus-kernel.c
src/libsystemd/sd-bus/test-bus-cleanup.c
src/libsystemd/sd-bus/test-bus-error.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-resolve/test-resolve.c
src/locale/localed.c
src/login/loginctl.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-seat.c
src/login/logind-user.c
src/machine-id-setup/machine-id-setup-main.c
src/machine/machine-dbus.c
src/machine/machinectl.c
src/machine/machined-dbus.c
src/network/networkctl.c
src/network/networkd-link-bus.c
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-util.c
src/network/networkd-util.h
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-setuid.c
src/nspawn/nspawn.c
src/resolve-host/resolve-host.c
src/resolve/RFCs [new file with mode: 0644]
src/resolve/dns-type.c
src/resolve/dns-type.h
src/resolve/resolved-bus.c
src/resolve/resolved-bus.h
src/resolve/resolved-conf.c
src/resolve/resolved-conf.h
src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-dnssec.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-question.c
src/resolve/resolved-dns-question.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dns-trust-anchor.h
src/resolve/resolved-dns-zone.c
src/resolve/resolved-gperf.gperf
src/resolve/resolved-link-bus.c [new file with mode: 0644]
src/resolve/resolved-link-bus.h [new file with mode: 0644]
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-llmnr.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-mdns.c
src/resolve/resolved-resolv-conf.c
src/resolve/resolved.c
src/resolve/test-dnssec-complex.c [new file with mode: 0644]
src/resolve/test-dnssec.c
src/shared/ask-password-api.c
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/dns-domain.c
src/shared/dns-domain.h
src/shared/dropin.c
src/shared/generator.c
src/shared/gpt.h
src/shared/resolve-util.c [new file with mode: 0644]
src/shared/resolve-util.h [new file with mode: 0644]
src/shared/switch-root.c
src/systemctl/systemctl.c
src/systemd/sd-messages.h
src/systemd/sd-network.h
src/systemd/sd-resolve.h
src/sysusers/sysusers.c
src/test/test-capability.c
src/test/test-dns-domain.c
src/test/test-execute.c
src/test/test-libudev.c
src/test/test-rlimit-util.c
src/test/test-string-util.c
src/test/test-unit-file.c
src/tmpfiles/tmpfiles.c
src/udev/collect/collect.c
src/udev/udev-builtin-blkid.c
src/udev/udev-builtin-input_id.c
src/udev/udev-builtin-net_id.c
src/udev/udev-node.c
src/udev/udev-watch.c
src/udev/udevd.c
src/vconsole/vconsole-setup.c
test/TEST-02-CRYPTSETUP/test.sh
test/TEST-03-JOBS/test-jobs.sh
test/TEST-04-JOURNAL/test-journal.sh
test/TEST-04-JOURNAL/test.sh
test/test-execute/exec-capabilityambientset-merge.service [new file with mode: 0644]
test/test-execute/exec-capabilityambientset.service [new file with mode: 0644]
test/test-functions
units/console-shell.service.m4.in
units/emergency.service.in
units/kmod-static-nodes.service.in
units/rescue.service.in

index 9d9f8cd178c96e16ded975a09045d09e0d5b2f19..eee106213b7049c9be95f5476a85435c16ff64b8 100644 (file)
@@ -3,5 +3,11 @@
 ; Mode can be nil, which gives default values.
 
 ((nil . ((indent-tabs-mode . nil)
-         (tab-width . 8)))
-)
+         (tab-width . 8)
+         (fill-column . 119)))
+ (c-mode . ((c-basic-offset . 8)
+            (eval . (c-set-offset 'substatement-open 0))
+            (eval . (c-set-offset 'statement-case-open 0))
+            (eval . (c-set-offset 'case-label 0))
+            (eval . (c-set-offset 'arglist-intro '++))
+            (eval . (c-set-offset 'arglist-close 0)))))
diff --git a/.vimrc b/.vimrc
index d8020f340469889cca4773ab97c7e1e009798453..b864268cd1a22f6903a96220e4d8ba872795c7a8 100644 (file)
--- a/.vimrc
+++ b/.vimrc
@@ -7,3 +7,4 @@ set tabstop=8
 set shiftwidth=8
 set expandtab
 set makeprg=GCC_COLORS=\ make
+set tw=119
index e91ecfdfdfc8509cac0c8742f62337608f3d5921..98769fbee8b05f6483db86efc5b95de3011bad9d 100644 (file)
@@ -1990,16 +1990,21 @@ endif
 
 if ENABLE_RESOLVED
 MANPAGES += \
+       man/dnssec-trust-anchors.d.5 \
        man/nss-resolve.8 \
        man/resolved.conf.5 \
        man/systemd-resolved.service.8
 MANPAGES_ALIAS += \
        man/libnss_resolve.so.2.8 \
        man/resolved.conf.d.5 \
-       man/systemd-resolved.8
+       man/systemd-resolved.8 \
+       man/systemd.negative.5 \
+       man/systemd.positive.5
 man/libnss_resolve.so.2.8: man/nss-resolve.8
 man/resolved.conf.d.5: man/resolved.conf.5
 man/systemd-resolved.8: man/systemd-resolved.service.8
+man/systemd.negative.5: man/dnssec-trust-anchors.d.5
+man/systemd.positive.5: man/dnssec-trust-anchors.d.5
 man/libnss_resolve.so.2.html: man/nss-resolve.html
        $(html-alias)
 
@@ -2009,6 +2014,12 @@ man/resolved.conf.d.html: man/resolved.conf.html
 man/systemd-resolved.html: man/systemd-resolved.service.html
        $(html-alias)
 
+man/systemd.negative.html: man/dnssec-trust-anchors.d.html
+       $(html-alias)
+
+man/systemd.positive.html: man/dnssec-trust-anchors.d.html
+       $(html-alias)
+
 endif
 
 if ENABLE_RFKILL
@@ -2434,6 +2445,7 @@ EXTRA_DIST += \
        man/coredumpctl.xml \
        man/crypttab.xml \
        man/daemon.xml \
+       man/dnssec-trust-anchors.d.xml \
        man/file-hierarchy.xml \
        man/halt.xml \
        man/hostname.xml \
index 00a92e34e03a843e608eec5d1590b9934a624e96..3eba35f9179f6fee4520eb6121784f14241a90e9 100644 (file)
@@ -694,29 +694,27 @@ man_MANS = \
 
 noinst_DATA += \
        $(HTML_FILES) \
-       $(HTML_ALIAS)
+       $(HTML_ALIAS) \
+       docs/html/man
+endif
 
 CLEANFILES += \
        $(man_MANS) \
        $(HTML_FILES) \
-       $(HTML_ALIAS)
+       $(HTML_ALIAS) \
+       docs/html/man
 
 docs/html/man:
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_LN)$(LN_S) -f ../../man $@
 
-noinst_DATA += \
-       docs/html/man
-
-CLEANFILES += \
-       docs/html/man
-
-if HAVE_PYTHON
 man/index.html: man/systemd.index.html
        $(AM_V_LN)$(LN_S) -f systemd.index.html $@
 
+if HAVE_PYTHON
 noinst_DATA += \
        man/index.html
+endif
 
 CLEANFILES += \
        man/index.html
@@ -745,11 +743,6 @@ CLEANFILES += \
        man/systemd.index.xml \
        man/systemd.directives.xml
 
-
-endif
-
-endif
-
 EXTRA_DIST += \
        $(filter-out man/systemd.directives.xml man/systemd.index.xml,$(XML_FILES)) \
        tools/make-man-index.py \
@@ -841,6 +834,8 @@ libbasic_la_SOURCES = \
        src/basic/mempool.h \
        src/basic/hashmap.c \
        src/basic/hashmap.h \
+       src/basic/hash-funcs.c \
+       src/basic/hash-funcs.h \
        src/basic/siphash24.c \
        src/basic/siphash24.h \
        src/basic/set.h \
@@ -1054,7 +1049,9 @@ libshared_la_SOURCES = \
        src/shared/machine-image.c \
        src/shared/machine-image.h \
        src/shared/machine-pool.c \
-       src/shared/machine-pool.h
+       src/shared/machine-pool.h \
+       src/shared/resolve-util.c \
+       src/shared/resolve-util.h
 
 if HAVE_UTMP
 libshared_la_SOURCES += \
@@ -5174,6 +5171,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-bus.h \
        src/resolve/resolved-link.h \
        src/resolve/resolved-link.c \
+       src/resolve/resolved-link-bus.c \
+       src/resolve/resolved-link-bus.h \
        src/resolve/resolved-llmnr.h \
        src/resolve/resolved-llmnr.c \
        src/resolve/resolved-mdns.h \
@@ -5293,6 +5292,9 @@ tests += \
        test-dns-domain \
        test-dnssec
 
+manual_tests += \
+       test-dnssec-complex
+
 test_dnssec_SOURCES = \
        src/resolve/test-dnssec.c \
        src/resolve/resolved-dns-packet.c \
@@ -5311,6 +5313,14 @@ test_dnssec_SOURCES = \
 test_dnssec_LDADD = \
        libshared.la
 
+test_dnssec_complex_SOURCES = \
+       src/resolve/test-dnssec-complex.c \
+       src/resolve/dns-type.c \
+       src/resolve/dns-type.h
+
+test_dnssec_complex_LDADD = \
+       libshared.la
+
 endif
 endif
 
@@ -5978,24 +5988,24 @@ src/%.c: src/%.gperf
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_GPERF)$(GPERF) < $< > $@
 
-src/%: src/%.m4
+src/%: src/%.m4 $(top_builddir)/config.status
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_M4)$(M4) -P $(M4_DEFINES) < $< > $@
 
-sysusers.d/%: sysusers.d/%.m4
+sysusers.d/%: sysusers.d/%.m4 $(top_builddir)/config.status
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_M4)$(M4) -P $(M4_DEFINES) < $< > $@
 
-tmpfiles.d/%: tmpfiles.d/%.m4
+tmpfiles.d/%: tmpfiles.d/%.m4 $(top_builddir)/config.status
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_M4)$(M4) -P $(M4_DEFINES) < $< > $@
 
 
-units/%: units/%.m4
+units/%: units/%.m4 $(top_builddir)/config.status
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_M4)$(M4) -P $(M4_DEFINES) -DFOR_SYSTEM=1 < $< > $@
 
-units/user/%: units/user/%.m4
+units/user/%: units/user/%.m4 $(top_builddir)/config.status
        $(AM_V_at)$(MKDIR_P) $(dir $@)
        $(AM_V_M4)$(M4) -P $(M4_DEFINES) -DFOR_USER=1 < $< > $@
 
@@ -6010,7 +6020,6 @@ EXTRA_DIST += \
        $(polkitpolicy_in_in_files)
 
 # ------------------------------------------------------------------------------
-if ENABLE_MANPAGES
 man/custom-entities.ent: configure.ac
        $(AM_V_GEN)$(MKDIR_P) $(dir $@)
        $(AM_V_GEN)(echo '<?xml version="1.0" encoding="utf-8" ?>' && \
@@ -6058,8 +6067,6 @@ define html-alias
        $(AM_V_LN)$(LN_S) -f $(notdir $<) $@
 endef
 
-endif
-
 EXTRA_DIST += \
        man/custom-html.xsl \
        man/custom-man.xsl
@@ -6285,14 +6292,14 @@ install-tree: all
        tree $(abs_srcdir)/install-tree
 
 # Let's run all tests of the test suite, but under valgrind. Let's
-# exclude the one perl script we have in there
+# exclude perl/python/shell scripts we have in there
 .PHONY: valgrind-tests
 valgrind-tests: $(TESTS)
-       $(AM_V_GEN)for f in $(filter-out %.pl, $^); do \
+       $(AM_V_GEN)for f in $(filter-out %.pl %.py, $^); do \
                if file $$f | grep -q shell; then \
                echo -e "$${x}Skipping non-binary $$f"; else \
                echo -e "$${x}Running $$f"; \
-               libtool --mode=execute valgrind -q --leak-check=full --max-stackframe=5242880 --error-exitcode=55 $(builddir)/$$f ; fi; \
+               $(LIBTOOL) --mode=execute valgrind -q --leak-check=full --max-stackframe=5242880 --error-exitcode=55 $(builddir)/$$f ; fi; \
                x="\n\n"; \
        done
 
diff --git a/README b/README
index bf67f8c03ed295d5b18bfb561fc86bcefc785c33..2fd7f7d5ce05963c132b056aca89ee910d8f15ed 100644 (file)
--- a/README
+++ b/README
@@ -237,10 +237,6 @@ SYSV INIT.D SCRIPTS:
         needs to look like, and provide an implementation at the marked places.
 
 WARNINGS:
-        systemd will freeze execution during boot if /etc/mtab exists
-        but is not a symlink to /proc/mounts. Please ensure that
-        /etc/mtab is a proper symlink.
-
         systemd will warn you during boot if /usr is on a different
         file system than /. While in systemd itself very little will
         break if /usr is on a separate partition, many of its
diff --git a/TODO b/TODO
index 369805fcee776961044f80e4456f1c6c0514bdb0..6aeb6c81638cbf091a1610421cda35fab102903b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -33,6 +33,21 @@ Janitorial Clean-ups:
 
 Features:
 
+* cache sd_event_now() result from before the first iteration...
+
+* remove Capabilities=, after all AmbientCapabilities= and CapabilityBoundingSet= should be enough.
+
+* support for the new copy_file_range() syscall
+
+* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
+
+* coredump logic should use prlimit() to query RLIMIT_CORE of the dumpee and honour it
+
+* Add a MaxRuntimeSec= setting for service units (or units in general) to terminate units after they ran for a certain
+  amount of time
+
+* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
+
 * PID1: find a way how we can reload unit file configuration for
   specific units only, without reloading the whole of systemd
 
diff --git a/catalog/systemd.hu.catalog b/catalog/systemd.hu.catalog
new file mode 100644 (file)
index 0000000..30d7691
--- /dev/null
@@ -0,0 +1,262 @@
+#  This file is part of systemd.
+#
+#  Copyright 2012 Lennart Poettering
+#  Copyright 2016 Gabor Kelemen
+#
+#  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/>.
+
+# Message catalog for systemd's own messages
+
+# The catalog format is documented on
+# http://www.freedesktop.org/wiki/Software/systemd/catalog
+
+# For an explanation why we do all this, see https://xkcd.com/1024/
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: A napló elindult
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszernapló folyamat elindult, megnyitotta írásra a naplófájlokat,
+és most készen áll kérések feldolgozására.
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: A napló leállt
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszernapló folyamat leállt, és bezárt minden jelenleg aktív naplófájlt.
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: Egy szolgáltatás üzenetei elnémítva
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:journald.conf(5)
+
+Egy szolgáltatás túl sok üzenetet naplózott adott idő alatt. A 
+szolgáltatástól származó üzenetek eldobásra kerültek.
+
+Ne feledje, hogy csak a kérdéses szolgáltatás üzenetei kerültek eldobásra,
+ más szolgáltatások üzeneteit ez nem befolyásolja. 
+
+Az üzenetek eldobását vezérlő korlátok az /etc/systemd/journald.conf
+RateLimitInterval= és RateLimitBurst= beállításaival adhatók meg.
+Részletekért lásd a journald.conf(5) man oldalt.
+
+-- e9bf28e6e834481bb6f48f548ad13606
+Subject: Naplóüzenetek vesztek el
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Kernelüzenetek vesztek el, mert a naplózó rendszer nem tudta elég gyorsan
+feldolgozni azokat.
+
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: Egy folyamat összeomlott: @COREDUMP_PID@ (@COREDUMP_COMM@)
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:core(5)
+
+Ez a folyamat: @COREDUMP_PID@ (@COREDUMP_COMM@) összeomlott, és core fájlt
+ írt ki.
+
+Ez általában programozási hibát jelez az összeomló programban, és 
+a szállítója felé kell bejelenteni.
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: Új munkamenet (@SESSION_ID@) létrehozva, felhasználója: @USER_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Létrejött egy új munkamenet @SESSION_ID@ azonosítóval ezen felhasználóhoz:
+@USER_ID@.
+
+A munkamenet vezető folyamata: @LEADER@.
+
+-- 3354939424b4456d9802ca8333ed424a
+Subject: Munkamenet (@SESSION_ID@) befejezve
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+A következő azonosítójú munkamenet befejeződött: @SESSION_ID@.
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: Elérhető egy új munkaállomás: @SEAT_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+Beállításra kerül és használható egy új munkaállomás: @SEAT_ID@.
+
+-- e7852bfe46784ed0accde04bc864c2d5
+Subject: A munkaállomás eltávolítva: @SEAT_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
+
+A munkaállomás el lett távolítva, és már nem érhető el: @SEAT_ID@
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: Időmódosítás
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszeróra beállítva @REALTIME@ ezredmásodpercre 1970. január 1. után.
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: Időzóna-módosítás erre: @TIMEZONE@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszer időzónája módosítva lett erre: @TIMEZONE@.
+
+-- b07a249cd024414a82dd00cd181378ff
+Subject: A rendszer indítása kész
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszerindításkor szükséges indításhoz sorba állított összes 
+rendszerszolgáltatás elindult. Ne feledje, hogy ez nem jelenti, hogy a 
+gép üresjáratban van, mivel egyes szolgáltatások még az indítás 
+befejezésével lehetnek elfoglalva.
+
+A kernel indítása @KERNEL_USEC@ ezredmásodpercet igényelt.
+
+A kiinduló RAM lemez indítása @INITRD_USEC@ ezredmásodpercet igényelt.
+
+A felhasználói programok indítása @USERSPACE_USEC@ ezredmásodpercet igényelt.
+
+-- 6bbd95ee977941e497c48be27c254128
+Subject: A rendszer „@SLEEP@” alvási állapotba lépett
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszer belépett ebbe az alvási állapotba: @SLEEP@.
+
+-- 8811e6df2a8e40f58a94cea26f8ebf14
+Subject: A rendszer „@SLEEP@” alvási állapotból kilépett
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A rendszer kilépett ebből az alvási állapotból: @SLEEP@.
+
+-- 98268866d1d54a499c4e98921d93bc40
+Subject: Rendszer leállítása kezdeményezve
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A systemd leállítása kezdeményezve. A leállítás megkezdődött, minden
+rendszerszolgáltatás befejeződik, minden fájlrendszer leválasztásra kerül.
+
+-- 7d4958e842da4a758f6c1cdc7b36dcc5
+Subject: A(z) @UNIT@ egység indítása megkezdődött
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység megkezdte az indulást.
+
+-- 39f53479d3a045ac8e11786248231fbf
+Subject: A(z) @UNIT@ egység befejezte az indulást
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység befejezte az indulást
+
+Az indítás eredménye: @RESULT@.
+
+-- de5b426a63be47a7b6ac3eaac82e2f6f
+Subject: A(z) @UNIT@ egység megkezdte a leállást
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység megkezdte a leállást.
+
+-- 9d1aaa27d60140bd96365438aad20286
+Subject: A(z) @UNIT@ egység befejezte a leállást
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység befejezte a leállást.
+
+-- be02cf6855d2428ba40df7e9d022f03d
+Subject: A(z) @UNIT@ egység hibát jelzett
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység hibát jelzett.
+
+Az eredmény: @RESULT@.
+
+-- d34d037fff1847e6ae669a370e694725
+Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység megkezdte a beállításainak újratöltését.
+
+-- 7b05ebc668384222baa8881179cfda54
+Subject: A(z) @UNIT@ egység befejezte a beállításainak újratöltését
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @UNIT@ egység befejezte a beállításainak újratöltését.
+
+Az eredmény: @RESULT@.
+
+-- 641257651c1b4ec9a8624d7a40a9e1e7
+Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A folyamat végrehajtása sikertelen volt, és hibát jelzett: @EXECUTABLE@.
+
+A folyamat által visszaadott hibaszám: @ERRNO@.
+
+-- 0027229ca0644181a76c4e92458afa2e
+Subject: Legalább egy üzenet nem továbbítható a rendszernaplónak
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+Legalább egy üzenet nem volt továbbítható a journald-vel párhuzamosan futó
+syslog szolgáltatásnak. Ez általában azt jelenti, hogy a syslog
+megvalósítás nem volt képes lépést tartani a sorba állított
+üzenetek sebességével.
+
+-- 1dee0369c7fc4736b7099b38ecb46ee7
+Subject: A csatolási pont nem üres
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A csatolási pontként megadott @WHERE@ könyvtár (második mező az /etc/fstab
+fájlban, vagy a Where= sor a systemd egységfájlban) nem üres. Ez nem
+akadályozza meg a csatolást, de a könyvtárban már meglévő fájlok
+elérhetetlenné válnak. A fájlok láthatóvá tételéhez csatolja
+az azokat tartalmazó fájlrendszert egy másodlagos helyre. 
+
+-- 24d8d4452573402496068381a6312df2
+Subject: Egy virtuális gép vagy konténer elindult
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) elindult, és 
+használatra kész.
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: Egy virtuális gép vagy konténer befejeződött
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+
+A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) leállt.
diff --git a/coccinelle/xsprintf.cocci b/coccinelle/xsprintf.cocci
new file mode 100644 (file)
index 0000000..401216a
--- /dev/null
@@ -0,0 +1,6 @@
+@@
+expression e, fmt;
+expression list vaargs;
+@@
+- snprintf(e, sizeof(e), fmt, vaargs);
++ xsprintf(e, fmt, vaargs);
index 3128ca867276e0821e28fc79fc626c58e11a88c3..228d5ee1dae84a85f67b8c36384174a2a5cd3198 100644 (file)
@@ -1301,9 +1301,9 @@ AM_CONDITIONAL(ENABLE_HWDB, [test x$enable_hwdb = xyes])
 # ------------------------------------------------------------------------------
 have_manpages=no
 AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable manpages]))
+AC_PATH_PROG([XSLTPROC], [xsltproc])
 AS_IF([test "x$enable_manpages" != xno], [
        have_manpages=yes
-       AC_PATH_PROG([XSLTPROC], [xsltproc])
        AS_IF([test -z "$XSLTPROC"],
              AC_MSG_ERROR([*** xsltproc is required for man pages]))
 ])
index a825e744e10fb09e1c8bcb0de797d0754f84847c..516abad246ab07ad3332b1be4ff1108a95cb9f5e 100644 (file)
@@ -1654,7 +1654,7 @@ bluetooth:v0224*
  ID_VENDOR_FROM_DATABASE=Comarch SA
 
 bluetooth:v0225*
- ID_VENDOR_FROM_DATABASE=Nestl Nespresso S.A.
+ ID_VENDOR_FROM_DATABASE=Nestlé Nespresso S.A.
 
 bluetooth:v0226*
  ID_VENDOR_FROM_DATABASE=Merlinia A/S
@@ -1780,7 +1780,7 @@ bluetooth:v024E*
  ID_VENDOR_FROM_DATABASE=Microtronics Engineering GmbH
 
 bluetooth:v024F*
- ID_VENDOR_FROM_DATABASE=Schneider Schreibgerte GmbH
+ ID_VENDOR_FROM_DATABASE=Schneider Schreibgeräte GmbH
 
 bluetooth:v0250*
  ID_VENDOR_FROM_DATABASE=Sapphire Circuits LLC
@@ -1897,7 +1897,7 @@ bluetooth:v0275*
  ID_VENDOR_FROM_DATABASE=Geotab
 
 bluetooth:v0276*
- ID_VENDOR_FROM_DATABASE=E.G.O. Elektro-Gertebau GmbH
+ ID_VENDOR_FROM_DATABASE=E.G.O. Elektro-Gerätebau GmbH
 
 bluetooth:v0277*
  ID_VENDOR_FROM_DATABASE=bewhere inc
@@ -2066,3 +2066,168 @@ bluetooth:v02AD*
 
 bluetooth:v02AE*
  ID_VENDOR_FROM_DATABASE=WeatherFlow, Inc.
+
+bluetooth:v02AF*
+ ID_VENDOR_FROM_DATABASE=Technicolor USA Inc.
+
+bluetooth:v02B0*
+ ID_VENDOR_FROM_DATABASE=Bestechnic(Shanghai),Ltd
+
+bluetooth:v02B1*
+ ID_VENDOR_FROM_DATABASE=Raden Inc
+
+bluetooth:v02B2*
+ ID_VENDOR_FROM_DATABASE=JouZen Oy
+
+bluetooth:v02B3*
+ ID_VENDOR_FROM_DATABASE=CLABER S.P.A.
+
+bluetooth:v02B4*
+ ID_VENDOR_FROM_DATABASE=Hyginex, Inc.
+
+bluetooth:v02B5*
+ ID_VENDOR_FROM_DATABASE=HANSHIN ELECTRIC RAILWAY CO.,LTD.
+
+bluetooth:v02B6*
+ ID_VENDOR_FROM_DATABASE=Schneider Electric
+
+bluetooth:v02B7*
+ ID_VENDOR_FROM_DATABASE=Oort Technologies LLC
+
+bluetooth:v02B8*
+ ID_VENDOR_FROM_DATABASE=Chrono Therapeutics
+
+bluetooth:v02B9*
+ ID_VENDOR_FROM_DATABASE=Rinnai Corporation
+
+bluetooth:v02BA*
+ ID_VENDOR_FROM_DATABASE=Swissprime Technologies AG
+
+bluetooth:v02BB*
+ ID_VENDOR_FROM_DATABASE=Koha.,Co.Ltd
+
+bluetooth:v02BC*
+ ID_VENDOR_FROM_DATABASE=Genevac Ltd
+
+bluetooth:v02BD*
+ ID_VENDOR_FROM_DATABASE=Chemtronics
+
+bluetooth:v02BE*
+ ID_VENDOR_FROM_DATABASE=Seguro Technology Sp. z o.o.
+
+bluetooth:v02BF*
+ ID_VENDOR_FROM_DATABASE=Redbird Flight Simulations
+
+bluetooth:v02C0*
+ ID_VENDOR_FROM_DATABASE=Dash Robotics
+
+bluetooth:v02C1*
+ ID_VENDOR_FROM_DATABASE=LINE Corporation
+
+bluetooth:v02C2*
+ ID_VENDOR_FROM_DATABASE=Guillemot Corporation
+
+bluetooth:v02C3*
+ ID_VENDOR_FROM_DATABASE=Techtronic Power Tools Technology Limited
+
+bluetooth:v02C4*
+ ID_VENDOR_FROM_DATABASE=Wilson Sporting Goods
+
+bluetooth:v02C5*
+ ID_VENDOR_FROM_DATABASE=Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) )
+
+bluetooth:v02C6*
+ ID_VENDOR_FROM_DATABASE=Ayatan Sensors
+
+bluetooth:v02C7*
+ ID_VENDOR_FROM_DATABASE=Electronics Tomorrow Limited
+
+bluetooth:v02C8*
+ ID_VENDOR_FROM_DATABASE=VASCO Data Security International, Inc.
+
+bluetooth:v02C9*
+ ID_VENDOR_FROM_DATABASE=PayRange Inc.
+
+bluetooth:v02CA*
+ ID_VENDOR_FROM_DATABASE=ABOV Semiconductor
+
+bluetooth:v02CB*
+ ID_VENDOR_FROM_DATABASE=AINA-Wireless Inc.
+
+bluetooth:v02CC*
+ ID_VENDOR_FROM_DATABASE=Eijkelkamp Soil & Water
+
+bluetooth:v02CD*
+ ID_VENDOR_FROM_DATABASE=BMA ergonomics b.v.
+
+bluetooth:v02CE*
+ ID_VENDOR_FROM_DATABASE=Teva Branded Pharmaceutical Products R&D, Inc.
+
+bluetooth:v02CF*
+ ID_VENDOR_FROM_DATABASE=Anima
+
+bluetooth:v02D0*
+ ID_VENDOR_FROM_DATABASE=3M
+
+bluetooth:v02D1*
+ ID_VENDOR_FROM_DATABASE=Empatica Srl
+
+bluetooth:v02D2*
+ ID_VENDOR_FROM_DATABASE=Afero, Inc.
+
+bluetooth:v02D3*
+ ID_VENDOR_FROM_DATABASE=Powercast Corporation
+
+bluetooth:v02D4*
+ ID_VENDOR_FROM_DATABASE=Secuyou ApS
+
+bluetooth:v02D5*
+ ID_VENDOR_FROM_DATABASE=OMRON Corporation
+
+bluetooth:v02D6*
+ ID_VENDOR_FROM_DATABASE=Send Solutions
+
+bluetooth:v02D7*
+ ID_VENDOR_FROM_DATABASE=NIPPON SYSTEMWARE CO.,LTD.
+
+bluetooth:v02D8*
+ ID_VENDOR_FROM_DATABASE=Neosfar
+
+bluetooth:v02D9*
+ ID_VENDOR_FROM_DATABASE=Fliegl Agrartechnik GmbH
+
+bluetooth:v02DA*
+ ID_VENDOR_FROM_DATABASE=Gilvader
+
+bluetooth:v02DB*
+ ID_VENDOR_FROM_DATABASE=Digi International Inc (R)
+
+bluetooth:v02DC*
+ ID_VENDOR_FROM_DATABASE=DeWalch Technologies, Inc.
+
+bluetooth:v02DD*
+ ID_VENDOR_FROM_DATABASE=Flint Rehabilitation Devices, LLC
+
+bluetooth:v02DE*
+ ID_VENDOR_FROM_DATABASE=Samsung SDS Co., Ltd.
+
+bluetooth:v02DF*
+ ID_VENDOR_FROM_DATABASE=Blur Product Development
+
+bluetooth:v02E0*
+ ID_VENDOR_FROM_DATABASE=University of Michigan
+
+bluetooth:v02E1*
+ ID_VENDOR_FROM_DATABASE=Victron Energy BV
+
+bluetooth:v02E2*
+ ID_VENDOR_FROM_DATABASE=NTT docomo
+
+bluetooth:v02E3*
+ ID_VENDOR_FROM_DATABASE=Carmanah Technologies Corp.
+
+bluetooth:v02E4*
+ ID_VENDOR_FROM_DATABASE=Bytestorm Ltd.
+
+bluetooth:v02E5*
+ ID_VENDOR_FROM_DATABASE=Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )
index 945c4461136cc2b02dd3e6660cad8725e76f2c08..577800e075b40919b2c37349631e5d4c65ad453c 100644 (file)
@@ -114,6 +114,13 @@ evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnInsp
  EVDEV_ABS_35=25:2000:22
  EVDEV_ABS_36=0:1351:28
 
+# Dell Latitude E6220
+evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6220*
+ EVDEV_ABS_00=76:1815:22
+ EVDEV_ABS_01=131:1330:30
+ EVDEV_ABS_35=76:1815:22
+ EVDEV_ABS_36=131:1330:30
+
 #########################################
 # Google
 #########################################
index aff9dcc0c2ae4de27d918d04147875ae66fe0026..01213b6069bb9d7e37ff691e8cde57c6cc4c3da9 100644 (file)
@@ -498,6 +498,13 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPProBook450G0:pvr*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:*
  KEYBOARD_KEY_b2=www                                    # Earth
 
+# HP ProBook 440 G3
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*440*G3*
+ KEYBOARD_KEY_92=brightnessdown
+ KEYBOARD_KEY_97=brightnessup
+ KEYBOARD_KEY_ee=switchvideomode
+ KEYBOARD_KEY_81=f20                                    # micmute
+
 ###########################################################
 # IBM
 ###########################################################
@@ -651,6 +658,11 @@ evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:pvr*
  KEYBOARD_KEY_f1=f21
 
+# Lenovo Thinkcentre M800z AIO machine
+# key_scancode 00 is KEY_MICMUTE
+keyboard:name:Microphone Mute Button:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*
+ KEYBOARD_KEY_00=f20
+
 # enhanced USB keyboard
 evdev:input:b0003v04B3p301B*
  KEYBOARD_KEY_90001=prog1 # ThinkVantage
index fb3cc9afa054d165d60e71299f25f40966d15991..54ace7cbc159d59c537de1614a9aa9fe6dc7c3f6 100644 (file)
@@ -308,6 +308,8 @@ mouse:usb:v046dpc046:name:Logitech USB Optical Mouse:
 mouse:usb:v046dpc05a:name:Logitech USB Optical Mouse:
 # Logitech USB Laser Mouse M-U0011-O rebranded as "terra Laser"
 mouse:usb:v046dpc065:name:Logitech USB Laser Mouse:
+# Logitech USB Laser Mouse M-U0007 [M500]
+mouse:usb:v046dpc069:name:Logitech USB Laser Mouse:
 # Logitech V500 Cordless Notebook Mouse
 mouse:usb:v046dpc510:name:Logitech USB Receiver:
 # Logitech M560 Wireless Mouse
@@ -339,8 +341,6 @@ mouse:usb:v046dp1024:name:Logitech M310:
 
 # Logitech USB Laser Mouse M-UAS144 [LS1 Laser Mouse]
 mouse:usb:v046dpc062:name:Logitech USB Laser Mouse:
-# Logitech USB Laser Mouse M-U0007
-mouse:usb:v046dpc069:name:Logitech USB Laser Mouse:
  MOUSE_DPI=1200@125
 
 # Logitech T620 (or, the soap)
index d8c1085021e5c5a645d89e2b9a5dc034b2113942..26d778d4dd30a0c94595a299cd4088426eb3551c 100644 (file)
@@ -448,7 +448,7 @@ ARRAY "s" {
     <example>
       <title>Invoking a Method</title>
 
-      <para>The following command invokes the
+      <para>The following command invokes the
       <literal>StartUnit</literal> method on the
       <literal>org.freedesktop.systemd1.Manager</literal>
       interface of the
diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml
new file mode 100644 (file)
index 0000000..4bdc167
--- /dev/null
@@ -0,0 +1,200 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+  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/>.
+-->
+
+<refentry id="dnssec-trust-anchors.d" conditional='ENABLE_RESOLVED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>dnssec-trust-anchors.d</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>dnssec-trust-anchors.d</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>dnssec-trust-anchors.d</refname>
+    <refname>systemd.positive</refname>
+    <refname>systemd.negative</refname>
+    <refpurpose>DNSSEC trust anchor configuration files</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/etc/dnssec-trust-anchors.d/*.positive</filename></para>
+    <para><filename>/run/dnssec-trust-anchors.d/*.positive</filename></para>
+    <para><filename>/usr/lib/dnssec-trust-anchors.d/*.positive</filename></para>
+    <para><filename>/etc/dnssec-trust-anchors.d/*.negative</filename></para>
+    <para><filename>/run/dnssec-trust-anchors.d/*.negative</filename></para>
+    <para><filename>/usr/lib/dnssec-trust-anchors.d/*.negative</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The DNSSEC trust anchor configuration files define positive
+    and negative trust anchors
+    <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    bases DNSSEC integrity proofs on.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Positive Trust Anchors</title>
+
+    <para>Positive trust anchor configuration files contain DNSKEY and
+    DS resource record definitions to use as base for DNSSEC integrity
+    proofs. See <ulink
+    url="https://tools.ietf.org/html/rfc4035#section-4.4">RFC 4035,
+    Section 4.4</ulink> for more information about DNSSEC trust
+    anchors.</para>
+
+    <para>Positive trust anchors are read from files with the suffix
+    <filename>.positive</filename> located in
+    <filename>/etc/dnssec-trust-anchors.d/</filename>,
+    <filename>/run/dnssec-trust-anchors.d/</filename> and
+    <filename>/usr/lib/dnssec-trust-anchors.d/</filename>. These
+    directories are searched in the specified order, and a trust
+    anchor file of the same name in an earlier path overrides a trust
+    anchor files in a later path. To disable a trust anchor file
+    shipped in <filename>/usr/lib/dnssec-trust-anchors.d/</filename>
+    it is sufficient to provide an identically-named file in
+    <filename>/etc/dnssec-trust-anchors.d/</filename> or
+    <filename>/run/dnssec-trust-anchors.d/</filename> that is either
+    empty or a symlink to <filename>/dev/null</filename> ("masked").</para>
+
+    <para>Positive trust anchor files are simple text files resembling
+    DNS zone files, as documented in <ulink
+    url="https://tools.ietf.org/html/rfc1035#section-5">RFC 1035, Section
+    5</ulink>. One DS or DNSKEY resource record may be listed per
+    line. Empty lines and lines starting with a semicolon
+    (<literal>;</literal>) are ignored and considered comments. A DS
+    resource record is specified like in the following example:</para>
+
+    <programlisting>. IN DS 19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5</programlisting>
+
+    <para>The first word specifies the domain, use
+    <literal>.</literal> for the root domain. The domain may be
+    specified with or without trailing dot, which is considered
+    equivalent. The second word must be <literal>IN</literal> the
+    third word <literal>DS</literal>. The following words specify the
+    key tag, signature algorithm, digest algorithm, followed by the
+    hex-encoded key fingerprint. See <ulink
+    url="https://tools.ietf.org/html/rfc4034#section-5">RFC 4034,
+    Section 5</ulink> for details about the precise syntax and meaning
+    of these fields.</para>
+
+    <para>Alternatively, DNSKEY resource records may be used to define
+    trust anchors, like in the following example:</para>
+
+    <programlisting>. IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0=</programlisting>
+
+    <para>The first word specifies the domain again, the second word
+    must be <literal>IN</literal>, followed by
+    <literal>DNSKEY</literal>. The subsequent words encode the DNSKEY
+    flags, protocol and algorithm fields, followed by the key data
+    encoded in Base64. See <ulink
+    url="https://tools.ietf.org/html/rfc4034#section-2">RFC 4034,
+    Section 2</ulink> for details about the precise syntax and meaning
+    of these fields.</para>
+
+    <para>If multiple DS or DNSKEY records are defined for the same
+    domain (possibly even in different trust anchor files), all keys
+    are used and are considered equivalent as base for DNSSEC
+    proofs.</para>
+
+    <para>Note that <filename>systemd-resolved</filename> will
+    automatically use a built-in trust anchor key for the Internet
+    root domain if no positive trust anchors are defined for the root
+    domain. In most cases it is hence unnecessary to define an
+    explicit key with trust anchor files. The built-in key is disabled
+    as soon as at least one trust anchor key for the root domain is
+    defined in trust anchor files.</para>
+
+    <para>It is generally recommended to encode trust anchors in DS
+    resource records, rather than DNSKEY resource records.</para>
+
+    <para>If a trust anchor specified via a DS record is found revoked
+    it is automatically removed from the trust anchor database for the
+    runtime. See <ulink url="https://tools.ietf.org/html/rfc5011">RFC
+    5011</ulink> for details about revoked trust anchors. Note that
+    <filename>systemd-resolved</filename> will not update its trust
+    anchor database from DNS servers automatically. Instead, it is
+    recommended to update the resolver software or update the new
+    trust anchor via adding in new trust anchor files.</para>
+
+    <para>The current DNSSEC trust anchor for the Internet's root
+    domain is available at the <ulink
+    url="https://data.iana.org/root-anchors/root-anchors.xml">IANA
+    Trust Anchor and Keys</ulink> page.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Negative Trust Anchors</title>
+
+    <para>Negative trust anchors define domains where DNSSEC
+    validation shall be turned off. Negative trust anchor files are
+    found at the same location as positive trust anchor files, and
+    follow the same overriding rules. They are text files with the
+    <filename>.negative</filename> suffix. Empty lines and lines whose
+    first character is <literal>;</literal> are ignored. Each line
+    specifies one domain name where DNSSEC validation shall be
+    disabled on.</para>
+
+    <para>Negative trust anchors are useful to support private DNS
+    subtrees that are not referenced from the Internet DNS hierarchy,
+    and not signed.</para>
+
+    <para><ulink url="https://tools.ietf.org/html/rfc7646">RFC
+    7646</ulink> for details on negative trust anchors.</para>
+
+    <para>If no negative trust anchor files are configured a built-in
+    set of well-known private DNS zone domains is used as negative
+    trust anchors.</para>
+
+    <para>It is also possibly to define per-interface negative trust
+    anchors using the <varname>DNSSECNegativeTrustAnchors=</varname>
+    setting in
+    <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    files.</para>
+  </refsect1>
+
+  <refsect1>
+      <title>See Also</title>
+      <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      </para>
+  </refsect1>
+
+</refentry>
index b7c5e6ac5c7c7fa886c6a00065c4bf02ed73ee11..2d345963d946d0e4c95097becce3ef9123419719 100644 (file)
     <literal>[Remote]</literal> section:</para>
 
     <variablelist>
+      <varlistentry>
+        <term><varname>Seal=</varname></term>
+
+        <listitem><para>Periodically sign the data in the journal using Forward Secure Sealing.
+        </para></listitem>
+      </varlistentry>
+
 
       <varlistentry>
         <term><varname>SplitMode=</varname></term>
index 309220632e3c5877b98c2c9890fbaae8b38d1b1e..42d5e006bb77736a91572ff6625cb88621b4a205 100644 (file)
@@ -91,6 +91,7 @@
         <term><varname>systemd.default_standard_output=</varname></term>
         <term><varname>systemd.default_standard_error=</varname></term>
         <term><varname>systemd.setenv=</varname></term>
+        <term><varname>systemd.machine_id=</varname></term>
         <listitem>
           <para>Parameters understood by the system and service
           manager to control system behavior. For details, see
index db72c2a01cad8a879276bb4afdd090e67d820a9c..d318ec54eca78335b06e5a1cc582866db7b2508b 100644 (file)
     at install time. Use
     <citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     to initialize it on mounted (but not booted) system images.</para>
+
+    <para>The machine-id may also be set, for example when network
+    booting, by setting the <varname>systemd.machine_id=</varname>
+    kernel command line parameter or passing the option
+    <option>--machine-id=</option> to systemd. A machine-id may not
+    be set to all zeros.</para>
   </refsect1>
 
   <refsect1>
index a7288c249b62374559493fe9ae831a5608e97e5c..f9395f3d7203e6b32e8fb4114fe6732dc9bf340d 100644 (file)
         <literal>checksum</literal> is specified, the download is
         checked for integrity after the transfer is complete, but no
         signatures are verified. If <literal>signature</literal> is
-        specified, the checksum is verified and the images's signature
+        specified, the checksum is verified and the image's signature
         is checked against a local keyring of trustable vendors. It is
         strongly recommended to set this option to
         <literal>signature</literal> if the server and protocol
index 4680b6a4e5923b2d62a22f1b30c0a8f20ae5fd32..3ab7fc4a11973d5c3192a410500f443c4cfefe1e 100644 (file)
         global setting is on.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>DNSSEC=</varname></term>
+        <listitem><para>Takes a boolean argument or
+        <literal>allow-downgrade</literal>. If true all DNS lookups are
+        DNSSEC-validated locally (excluding LLMNR and Multicast
+        DNS). If a response for a lookup request is detected invalid
+        this is returned as lookup failure to applications. Note that
+        this mode requires a DNS server that supports DNSSEC. If the
+        DNS server does not properly support DNSSEC all validations
+        will fail. If set to <literal>allow-downgrade</literal> DNSSEC
+        validation is attempted, but if the server does not support
+        DNSSEC properly, DNSSEC mode is automatically disabled. Note
+        that this mode makes DNSSEC validation vulnerable to
+        "downgrade" attacks, where an attacker might be able to
+        trigger a downgrade to non-DNSSEC mode by synthesizing a DNS
+        response that suggests DNSSEC was not supported. If set to
+        false, DNS lookups are not DNSSEC validated.</para>
+
+        <para>Note that DNSSEC validation requires retrieval of
+        additional DNS data, and thus results in a small DNS look-up
+        time penalty.</para>
+
+        <para>DNSSEC requires knowledge of "trust anchors" to prove
+        data integrity. The trust anchor for the Internet root domain
+        is built into the resolver, additional trust anchors may be
+        defined with
+        <citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        Trust anchors may change in regular intervals, and old trust
+        anchors may be revoked. In such a case DNSSEC validation is
+        not possible until new trust anchors are configured locally or
+        the resolver software package is updated with the new root
+        trust anchor. In effect, when the built-in trust anchor is
+        revoked and <varname>DNSSEC=</varname> is true, all further
+        lookups will fail, as it cannot be proved anymore whether
+        lookups are correctly signed, or validly unsigned. If
+        <varname>DNSSEC=</varname> is set to
+        <literal>allow-downgrade</literal> the resolver will
+        automatically turn off DNSSEC validation in such a case.</para>
+
+        <para>Client programs looking up DNS data will be informed
+        whether lookups could be verified using DNSSEC, or whether the
+        returned data could not be verified (either because the data
+        was found unsigned in the DNS, or the DNS server did not
+        support DNSSEC or no appropriate trust anchors were known). In
+        the latter case it is assumed that client programs employ a
+        secondary scheme to validate the returned DNS data, should
+        this be required.</para>
+
+        <para>It is recommended to set <varname>DNSSEC=</varname> to
+        true on systems where it is known that the DNS server supports
+        DNSSEC correctly, and where software or trust anchor updates
+        happen regularly. On other systems it is recommended to set
+        <varname>DNSSEC=</varname> to
+        <literal>allow-downgrade</literal>.</para>
+
+        <para>In addition to this global DNSSEC setting
+        <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        also maintains per-interface DNSSEC settings. For system DNS
+        servers (see above), only the global DNSSEC setting is in
+        effect. For per-interface DNS servers the per-interface
+        setting is in effect, unless it is unset in which case the
+        global setting is used instead.</para>
+
+        <para>Site-private DNS zones generally conflict with DNSSEC
+        operation, unless a negative (if the private zone is not
+        signed) or positive (if the private zone is signed) trust
+        anchor is configured for them. If
+        <literal>allow-downgrade</literal> mode is selected, it is
+        attempted to detect site-private DNS zones using top-level
+        domains (TLDs) that are not known by the DNS root server. This
+        logic does not work in all private zone setups.</para>
+
+        <para>Defaults to off.</para>
+        </listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>4</manvolnum></citerefentry>
       </para>
   </refsect1>
index 47989f4421b5cf496fb87ad4a118cb3542df9150..fc615f09067436bd4859f373220a33c773fcd9a2 100644 (file)
 
       <listitem><para>Event sources may be assigned a 64bit priority
       value, that controls the order in which event sources are
-      dispatched if multiple are pending simultanously. See
+      dispatched if multiple are pending simultaneously. See
       <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
 
       <listitem><para>The event loop may automatically send watchdog
index aec12bda164246338bb653f0eb0a5dec84b88376..3bcda46656e3b5b4afd51a7925540c956007494c 100644 (file)
     modified by the caller.</para>
 
     <para>All functions that take a <parameter>char***</parameter>
-    parameter will store the answer there as an address of a an array
+    parameter will store the answer there as an address of an array
     of strings. Each individual string is NUL-terminated, and the
     array is NULL-terminated as a whole. It will be valid as long as
     <parameter>c</parameter> remains valid, and should not be freed or
index d4b180cf03c591c49c1dcbffce92ce63abec6ae7..bc732db7fa4a7d6de1331e7627180800273ac5db 100644 (file)
     <constant>SD_EVENT_OFF</constant> with
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
-    <para>If the the second parameter of
+    <para>If the second parameter of
     <function>sd_event_add_child()</function> is passed as NULL no
     reference to the event source object is returned. In this case the
     event source is considered "floating", and will be destroyed
index 6a13ede76e69d4f08df019a7d7da58c17bb72712..d9ebd3b179a85401eef2a2fc2f591926602805e3 100644 (file)
     handler will be called once
     (<constant>SD_EVENT_ONESHOT</constant>). Note that if the event
     source is set to <constant>SD_EVENT_ON</constant> the event loop
-    will never go to sleep again, but continously call the handler,
+    will never go to sleep again, but continuously call the handler,
     possibly interleaved with other event sources.</para>
 
     <para><function>sd_event_add_post()</function> adds a new event
index 4cc0428e299c817ed909b6d375b68633f6d22996..c3749164cdf28fb029d92ec469734c54ba0296cb 100644 (file)
     returned in the <parameter>source</parameter> parameter. The
     <parameter>fd</parameter> parameter takes the UNIX file descriptor
     to watch, which may refer to a socket, a FIFO, a message queue, a
-    serial connection, a character device or any other file descriptor
-    compatible with Linux <citerefentry
-    project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
-    <parameter>events</parameter> parameter takes a bit mask of I/O
-    events to watch the file descriptor for, a combination of the
-    following event flags: <constant>EPOLLIN</constant>,
-    <constant>EPOLLOUT</constant>, <constant>EPOLLRDHUP</constant>,
-    <constant>EPOLLPRI</constant> and <constant>EPOLLET</constant>,
-    see <citerefentry
-    project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    serial connection, a character device, or any other file descriptor
+    compatible with Linux
+    <citerefentry project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
+    <parameter>events</parameter> parameter takes a bit mask of events
+    to watch for, a combination of the following event flags:
+    <constant>EPOLLIN</constant>, <constant>EPOLLOUT</constant>,
+    <constant>EPOLLRDHUP</constant>, <constant>EPOLLPRI</constant>,
+    and <constant>EPOLLET</constant>, see
+    <citerefentry project='man-pages'><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for details. The <parameter>handler</parameter> shall reference a
-    function to call when the I/O event source is triggered. The
-    handler function will be passed the
-    <parameter>userdata</parameter> pointer, which may be chosen
-    freely by the caller. The handler will also be passed the file
-    descriptor the event was seen on as well as the actual event flags
-    seen. It's generally a subset of the events watched, however may
-    additionally have <constant>EPOLLERR</constant> and
-    <constant>EPOLLHUP</constant> set.</para>
-
-    <para>By default, the I/O event source will stay enabled
-    continously (<constant>SD_EVENT_ON</constant>), but this may be
+    function to call when the event source is triggered. The
+    <parameter>userdata</parameter> pointer will be passed to the
+    handler function, and may be chosen freely by the caller. The
+    handler will also be passed the file descriptor the event was seen
+    on, as well as the actual event flags. It's generally a subset of
+    the events watched, however may additionally include
+    <constant>EPOLLERR</constant> and <constant>EPOLLHUP</constant>.
+    </para>
+
+    <para>By default, an event source will stay enabled
+    continuously (<constant>SD_EVENT_ON</constant>), but this may be
     changed with
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     If the handler function returns a negative error code, it will be
     disabled after the invocation, even if the
     <constant>SD_EVENT_ON</constant> mode was requested before. Note
-    that an I/O event source set to <constant>SD_EVENT_ON</constant> will
-    fire continously unless data is read or written to the file
-    descriptor in order to reset the mask of events seen.
+    that an event source set to <constant>SD_EVENT_ON</constant> will
+    fire continuously unless data is read from or written to the file
+    descriptor to reset the mask of events seen.
     </para>
 
     <para>Setting the I/O event mask to watch for to 0 does not mean
     <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     but note that the event source is only removed from the event loop
     when all references to the event source are dropped. To make sure
-    an event source does not fire anymore, even when there's still a
-    reference to it kept, consider setting the event source to
-    <constant>SD_EVENT_OFF</constant> with
-    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+    an event source does not fire anymore, even if it is still referenced,
+    disable the event source using
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    with <constant>SD_EVENT_OFF</constant>.</para>
 
-    <para>If the the second parameter of
-    <function>sd_event_add_io()</function> is passed as NULL no
-    reference to the event source object is returned. In this case the
-    event source is considered "floating", and will be destroyed
-    implicitly when the event loop itself is destroyed.</para>
+    <para>If the second parameter of
+    <function>sd_event_add_io()</function> is
+    <constant>NULL</constant> no reference to the event source object
+    is returned. In this case the event source is considered
+    "floating", and will be destroyed implicitly when the event loop
+    itself is destroyed.</para>
 
     <para>It is recommended to use
     <function>sd_event_add_io()</function> only in conjunction with
     ensure that all I/O operations from invoked handlers are properly
     asynchronous and non-blocking. Using file descriptors without
     <constant>O_NONBLOCK</constant> might result in unexpected
-    starving of other event sources. See <citerefentry
-    project='man-pages'><refentrytitle>fcntl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    starvation of other event sources. See
+    <citerefentry project='man-pages'><refentrytitle>fcntl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for details on enabling <constant>O_NONBLOCK</constant> mode.</para>
 
     <para><function>sd_event_source_get_io_events()</function> retrieves
-    the configured I/O event mask to watch of an I/O event source created
+    the configured mask of watched I/O events of an event source created
     previously with <function>sd_event_add_io()</function>. It takes
     the event source object and a pointer to a variable to store the
-    event mask in.</para>
+    mask in.</para>
 
-    <para><function>sd_event_source_set_io_events()</function> changes the
-    configured I/O event mask to watch of an I/O event source created previously
-    with <function>sd_event_add_io()</function>. It takes the event
-    source object and the new event mask to set.</para>
+    <para><function>sd_event_source_set_io_events()</function>
+    configures the mask of watched I/O events of an event source created
+    previously with <function>sd_event_add_io()</function>. It takes the
+    event source object and the new event mask.</para>
 
     <para><function>sd_event_source_get_io_revents()</function>
     retrieves the I/O event mask of currently seen but undispatched
-    events from an I/O event source created previously with
+    events from an event source created previously with
     <function>sd_event_add_io()</function>. It takes the event source
     object and a pointer to a variable to store the event mask
     in. When called from a handler function on the handler's event
     source types, the latter only to I/O event sources.</para>
 
     <para><function>sd_event_source_get_io_fd()</function> retrieves
-    the UNIX file descriptor of an I/O event source created previously
+    the UNIX file descriptor of an event source created previously
     with <function>sd_event_add_io()</function>. It takes the event
-    source object and returns the positive file descriptor in the return
-    value, or a negative error number on error (see below).</para>
+    source object and returns the non-negative file descriptor
+    or a negative error number on error (see below).</para>
 
     <para><function>sd_event_source_set_io_fd()</function>
     changes the UNIX file descriptor of an I/O event source created
     previously with <function>sd_event_add_io()</function>. It takes
-    the event source object and the new file descriptor to set.</para>
+    the event source object and the new file descriptor.</para>
   </refsect1>
 
   <refsect1>
 
     <para>On success, these functions return 0 or a positive
     integer. On failure, they return a negative errno-style error
-    code. </para>
+    code.</para>
   </refsect1>
 
   <refsect1>
     <title>Errors</title>
 
-    <para>Returned errors may indicate the following problems:</para>
+    <para>Returned values may indicate the following problems:</para>
 
     <variablelist>
       <varlistentry>
index b5312735d27b8bf2359b06dc05e7b98752bc7655..e98f1d2682172bd492da2e61026fb7af16377984 100644 (file)
     <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     but note that the event source is only removed from the event loop
     when all references to the event source are dropped. To make sure
-    an event source does not fire anymore, even when there's still a
-    reference to it kept, consider setting the event source to
-    <constant>SD_EVENT_OFF</constant> with
-    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
-
-    <para>If the the second parameter of
-    <function>sd_event_add_signal()</function> is passed as NULL no
-    reference to the event source object is returned. In this case the
-    event source is considered "floating", and will be destroyed
-    implicitly when the event loop itself is destroyed.</para>
-
-    <para><function>sd_event_source_get_signal()</function> retrieves
-    the configured UNIX process signal number of a signal event source
-    created previously with
-    <function>sd_event_add_signal()</function>. It takes the event
-    source object as the <parameter>source</parameter>
+    an event source does not fire anymore, even if it is still referenced,
+    disable the event source using
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    with <constant>SD_EVENT_OFF</constant>.</para>
+
+    <para>If the second parameter of
+    <function>sd_event_add_signal()</function> is
+    <constant>NULL</constant> no reference to the event source object
+    is returned. In this case the event source is considered
+    "floating", and will be destroyed implicitly when the event loop
+    itself is destroyed.</para>
+
+    <para><function>sd_event_source_get_signal()</function> returns
+    the configured signal number of an event source created previously
+    with <function>sd_event_add_signal()</function>. It takes the
+    event source object as the <parameter>source</parameter>
     parameter.</para>
-
   </refsect1>
 
   <refsect1>
 
     <para>On success, these functions return 0 or a positive
     integer. On failure, they return a negative errno-style error
-    code. </para>
+    code.</para>
   </refsect1>
 
   <refsect1>
         <term><constant>-EINVAL</constant></term>
 
         <listitem><para>An invalid argument has been passed.</para></listitem>
-
       </varlistentry>
 
       <varlistentry>
 
         <listitem><para>A handler is already installed for this
         signal or the signal was not blocked previously.</para></listitem>
-
       </varlistentry>
 
       <varlistentry>
         <term><constant>-ESTALE</constant></term>
 
         <listitem><para>The event loop is already terminated.</para></listitem>
-
       </varlistentry>
 
       <varlistentry>
         <term><constant>-ECHILD</constant></term>
 
         <listitem><para>The event loop has been created in a different process.</para></listitem>
-
       </varlistentry>
 
       <varlistentry>
index df38f52fc9721a06d088917090c3d57505280054..142fa80f8f41229f4193ed8aa1acb35cff506c39 100644 (file)
     clock identifier, one of <constant>CLOCK_REALTIME</constant>,
     <constant>CLOCK_MONOTONIC</constant>,
     <constant>CLOCK_BOOTTIME</constant>,
-    <constant>CLOCK_REALTIME_ALARM</constant> or
+    <constant>CLOCK_REALTIME_ALARM</constant>, or
     <constant>CLOCK_BOOTTIME_ALARM</constant>. See
     <citerefentry><refentrytitle>timerfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for details regarding the various types of clocks. The
-    <parameter>usec</parameter> parameter takes a time value in
-    microseconds (µs), relative to the clock's epoch, specifying when
-    the timer shall elapse the earliest. If a time that already lies
-    in the past is specified (including 0), the timer source is
-    dispatched immediately in the next event loop iterations. The
-    <parameter>accuracy</parameter> parameter takes an additional
-    accuracy value in µs specifying a time the timer event may be
-    delayed. Specify 0 for selecting the default accuracy
-    (250ms). Specify 1µs for most accurate timers. Consider specifying
-    60000000µs or larger (1min) for long-running events that may be
+    <parameter>usec</parameter> parameter specifies the earliest time,
+    in microseconds (µs), relative to the clock's epoch, when
+    the timer shall be triggered. If a time already in the past is
+    specified (including <constant>0</constant>), this timer source
+    "fires" immediately and is ready to be dispatched. The
+    <parameter>accuracy</parameter> parameter specifies an additional
+    accuracy value in µs specifying how much the timer event may be
+    delayed. Use <constant>0</constant> to select the default accuracy
+    (250ms). Use 1µs for maximum accuracy. Consider specifying
+    60000000µs (1min) or larger for long-running events that may be
     delayed substantially. Picking higher accuracy values allows the
-    system to coalesce timer events more aggressively, thus improving
+    system to coalesce timer events more aggressively, improving
     power efficiency. The <parameter>handler</parameter> parameter
     shall reference a function to call when the timer elapses. The
     handler function will be passed the
     <parameter>userdata</parameter> pointer, which may be chosen
     freely by the caller. The handler is also passed the configured
-    time it was triggered, however it might actually have been called
-    at a slightly later time, subject to the specified accuracy value,
+    trigger time, even if it is actually called
+    slightly later, subject to the specified accuracy value,
     the kernel timer slack (see
-    <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>)
+    <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>),
     and additional scheduling latencies. To query the actual time the
     handler was called use
     <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
     disabled after the invocation, even if the
     <constant>SD_EVENT_ON</constant> mode was requested before. Note
     that a timer event set to <constant>SD_EVENT_ON</constant> will
-    fire continously unless its configured time is updated using
+    fire continuously unless its configured time is updated using
     <function>sd_event_source_set_time()</function>.
     </para>
 
     <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     but note that the event source is only removed from the event loop
     when all references to the event source are dropped. To make sure
-    an event source does not fire anymore, even when there's still a
-    reference to it kept, consider setting the event source to
-    <constant>SD_EVENT_OFF</constant> with
-    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
-
-    <para>If the the second parameter of
-    <function>sd_event_add_time()</function> is passed as NULL no
-    reference to the event source object is returned. In this case the
-    event source is considered "floating", and will be destroyed
-    implicitly when the event loop itself is destroyed.</para>
+    an event source does not fire anymore, even if it is still referenced,
+    disable the event source using
+    <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    with <constant>SD_EVENT_OFF</constant>.</para>
+
+    <para>If the second parameter of
+    <function>sd_event_add_time()</function> is
+    <constant>NULL</constant> no reference to the event source object
+    is returned. In this case the event source is considered
+    "floating", and will be destroyed implicitly when the event loop
+    itself is destroyed.</para>
 
     <para>If the <parameter>handler</parameter> to
-    <function>sd_event_add_time()</function> is passed as NULL, and
-    the event source fires, this will be considered a request to exit
-    the event loop. In this case, the <parameter>userdata</parameter>
-    parameter, cast to an integer is used for the exit code passed to
+    <function>sd_event_add_time()</function> is
+    <constant>NULL</constant>, and the event source fires, this will
+    be considered a request to exit the event loop. In this case, the
+    <parameter>userdata</parameter> parameter, cast to an integer, is
+    used for the exit code passed to
     <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
     <para>Use <constant>CLOCK_BOOTTIME_ALARM</constant> and
     <para>In order to set up relative timers (that is, relative to the
     current time), retrieve the current time via
     <citerefentry><refentrytitle>sd_event_now</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    add the desired timespan to sleep to it, and pass the result as
+    add the desired timespan to it, and use the result as
     the <parameter>usec</parameter> parameter to
     <function>sd_event_add_time()</function>.</para>
 
     latency will keep accumulating on the timer.</para>
 
     <para><function>sd_event_source_get_time()</function> retrieves
-    the configured time value of a timer event source created
+    the configured time value of an event source created
     previously with <function>sd_event_add_time()</function>. It takes
     the event source object and a pointer to a variable to store the
-    time, relative to the selected clock's epoch, in µs in.</para>
+    time in, relative to the selected clock's epoch, in µs.</para>
 
     <para><function>sd_event_source_set_time()</function> changes the
-    configured time value of a timer event source created previously
-    with <function>sd_event_add_time()</function>. It takes the event
-    source object and a time relative to the selected clock's
-    epoch, in µs.</para>
+    time of an event source created previously with
+    <function>sd_event_add_time()</function>. It takes the event
+    source object and a time relative to the selected clock's epoch,
+    in µs.</para>
 
     <para><function>sd_event_source_get_time_accuracy()</function>
-    retrieves the configured accuracy value of a timer event source
+    retrieves the configured accuracy value of a event source
     created previously with <function>sd_event_add_time()</function>. It
     takes the event source object and a pointer to a variable to store
-    the accuracy in µs in.</para>
+    the accuracy in. The accuracy is specified in µs.</para>
 
     <para><function>sd_event_source_set_time_accuracy()</function>
     changes the configured accuracy of a timer event source created
     previously with <function>sd_event_add_time()</function>. It takes
-    the event source object and an accuracy, in µs.</para>
+    the event source object and accuracy, in µs.</para>
 
     <para><function>sd_event_source_get_time_clock()</function>
-    retrieves the configured clock of a timer event source created
+    retrieves the configured clock of a event source created
     previously with <function>sd_event_add_time()</function>. It takes
     the event source object and a pointer to a variable to store the
     clock identifier in.</para>
   <refsect1>
     <title>Errors</title>
 
-    <para>Returned errors may indicate the following problems:</para>
+    <para>Returned values may indicate the following problems:</para>
 
     <variablelist>
       <varlistentry>
index 4f34f3b122fedbfa08d5dc73bfbfee816ba6d7d4..9846a3eaf4a6fd6557226899cc8d73b49cc778a6 100644 (file)
@@ -76,7 +76,7 @@
     exit. The <parameter>code</parameter> parameter may be any integer
     value and is returned as-is by
     <citerefentry><refentrytitle>sd_event_loop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    after the last event loop iteration. It may also be be queried
+    after the last event loop iteration. It may also be queried
     using <function>sd_event_get_exit_code()</function>, see
     below. </para>
 
index f577e44c0e3b979839d61c469626e9002cf94cf6..2c83b0bcb5af686f88301b09aaa6cf2fdf35e821 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_event_now()</function> returns the timestamp
-    the most recent event loop iteration began. This timestamp is
-    taken right after after returning from the event sleep, and before
+    <para><function>sd_event_now()</function> returns the time when
+    the most recent event loop iteration began. A timestamp
+    is taken right after returning from the event sleep, and before
     dispatching any event sources. The <parameter>event</parameter>
-    parameter takes the even loop object to retrieve the timestamp
+    parameter specifies the event loop object to retrieve the timestamp
     from. The <parameter>clock</parameter> parameter specifies the clock to
     retrieve the timestamp for, and is one of
-    <constant>CLOCK_REALTIME</constant> (or its equivalent
+    <constant>CLOCK_REALTIME</constant> (or equivalently
     <constant>CLOCK_REALTIME_ALARM</constant>),
-    <constant>CLOCK_MONOTONIC</constant> or
-    <constant>CLOCK_BOOTTIME</constant> (or its equivalent
-    <constant>CLOCK_BOOTTIME_ALARM</constant>), see <citerefentry
-    project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    <constant>CLOCK_MONOTONIC</constant>, or
+    <constant>CLOCK_BOOTTIME</constant> (or equivalently
+    <constant>CLOCK_BOOTTIME_ALARM</constant>), see
+    <citerefentry project='man-pages'><refentrytitle>clock_gettime</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for more information on the various clocks. The retrieved
     timestamp is stored in the <parameter>usec</parameter> parameter,
     in µs since the clock's epoch. If this function is invoked before
-    the first event loop iteration the current time is returned, as
+    the first event loop iteration, the current time is returned, as
     reported by <function>clock_gettime()</function>. To distinguish
     this case from a regular invocation the return value will be
-    positive non-zero in this case, while it is zero when the returned
-    timestamp refers to the actual event loop iteration.</para>
+    positive, and zero when the returned timestamp refers to an actual
+    event loop iteration.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
     <para>If the first event loop iteration has not run yet
-    <function>sd_event_now()</function> returns the requested
-    timestamp in <parameter>usec</parameter> and returns a positive,
-    non-zero return value. Otherwise, on success it will return the
-    iteration's timestamp in <parameter>usec</parameter> and 0 as
-    return value. On failure, the call returns a negative errno-style
+    <function>sd_event_now()</function> writes current time to
+    <parameter>usec</parameter> and returns a positive return value.
+    Otherwise, it will write the requested timestamp to <parameter>usec</parameter>
+    and return 0. On failure, the call returns a negative errno-style
     error code.</para>
   </refsect1>
 
   <refsect1>
     <title>Errors</title>
 
-    <para>Returned errors may indicate the following problems:</para>
+    <para>Returned values may indicate the following problems:</para>
 
     <variablelist>
       <varlistentry>
 
       </varlistentry>
 
+      <varlistentry>
+        <term><constant>-EOPNOTSUPP</constant></term>
+
+        <listitem><para>Unsupported clock type.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><constant>-ECHILD</constant></term>
 
         <listitem><para>The event loop object was created in a
         different process.</para></listitem>
       </varlistentry>
-
     </variablelist>
   </refsect1>
 
index 74c02e87bb39b096bc180a2fa7d641530defff2b..6844f29a4959ab1b36ceeb1f81943cff1506b531 100644 (file)
     with calls such as
     <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>. However,
-    depending on the event source type they are enabled continously
+    depending on the event source type they are enabled continuously
     (<constant>SD_EVENT_ON</constant>) or only for a single invocation
     of the event source handler
     (<constant>SD_EVENT_ONESHOT</constant>). For details see the
index 7066a5530607922ab51e3651bb10b29619f2deeb..24861d01d9e290abcb48376c90d0de3d2c7ce0d8 100644 (file)
@@ -71,7 +71,7 @@
     <title>Description</title>
 
     <para><function>sd_event_source_set_prepare()</function> may be
-    used to set a prepartion callback for the event source object
+    used to set a preparation callback for the event source object
     specified as <parameter>source</parameter>. The callback function
     specified as <parameter>callback</parameter> will be invoked
     immediately before the event loop goes to sleep to wait for
index cc0f5a0103cfa80c5d3f1743bf9ae6f5a194ffb7..9234f4233eeb437c733e9e1a8972d89069cd8db6 100644 (file)
     dispatched is undefined, but the event loop generally tries to
     dispatch them in the order it learnt about events on them. As the
     backing kernel primitives do not provide accurate information
-    about the order in which events occured this is not necessarily
+    about the order in which events occurred this is not necessarily
     reliable. However, it is guaranteed that if events are seen on
     multiple same-priority event sources at the same time, each one is
     not dispatched again until all others have been dispatched
index 1eefa807003acad4d81b8eb7e5671bddffc7fb34..f2aea00e985c0dc05cf358445111918f0d922c85 100644 (file)
     and
     <citerefentry><refentrytitle>sd_event_loop</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     for higher-level functions that execute individual but complete
-    iterations of an event loop or run it continously.</para>
+    iterations of an event loop or run it continuously.</para>
 
     <para><function>sd_event_prepare()</function> checks for pending
     events and arms necessary timers. If any events are ready to be
         <term><constant>SD_EVENT_PREPARING</constant></term>
 
         <listitem><para>An event source is currently being prepared,
-        i.e. the preparation handler is currently being excuted, as
+        i.e. the preparation handler is currently being executed, as
         set with
         <citerefentry><refentrytitle>sd_event_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
         state is only seen in the event source preparation handler
index dbf63304530c84e1e6e8f2b8ff0dae8e04fb5c75..bd6cfdcd29cf910a0b83b17cbb96b49406461a91 100644 (file)
         multiple file descriptors are submitted at once, the specified
         name will be assigned to all of them. In order to assign
         different names to submitted file descriptors, submit them in
-        seperate invocations of
+        separate invocations of
         <function>sd_pid_notify_with_fds()</function>. The name may
         consist of any ASCII character, but must not contain control
         characters or <literal>:</literal>. It may not be longer than
index 6e1d505dce9e5e49398080c0409250290b69fe80..c5e6ddab024b00e082b9f16003011c2741dfea0b 100644 (file)
     <function>sd_seat_get_sessions()</function>,
     <function>sd_seat_can_multi_session()</function>,
     <function>sd_seat_can_tty()</function> and
-    <function>sd_seat_can_grapical()</function> interfaces are
+    <function>sd_seat_can_graphical()</function> interfaces are
     available as a shared library, which can be compiled and linked to
     with the
     <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
index cf914f4cb2b807f41aa50938f8aae3ba15b5cd3a..a55e06059a6864b19862b25d4ee5dd02a00cad19 100644 (file)
@@ -1176,7 +1176,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
                   </row>
                   <row>
                     <entry><literal>bad</literal></entry>
-                    <entry>Unit file is invalid or another error occured. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
+                    <entry>Unit file is invalid or another error occurred. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
                     <entry>&gt; 0</entry>
                   </row>
                 </tbody>
index 6df2248578f8155268096e269de43feb99f984ef..e32ac268505f2b7c915c7ba718e8e08c947b9e41 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><constant>application/event-stream</constant></term>
+        <term><constant>text/event-stream</constant></term>
 
         <listitem><para>Entries are formatted as JSON data structures,
         wrapped in a format suitable for <ulink
index 43d568c6f7ed1626511a9a0c3c6488f09dfe6640..8e1ca1c092ad01e043f015737d11d835bce5ae5d 100644 (file)
       <listitem><para>Multi-label names are routed to all local
       interfaces that have a DNS sever configured, plus the globally
       configured DNS server if there is one. Address lookups from the
-      link-local addres range are never routed to
+      link-local address range are never routed to
       DNS.</para></listitem>
     </itemizedlist>
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
index 5f98ef163c04cfc855ad433d36a998ba96e8d4b7..f0f77c509159e524838cfc62ca3aca7041305a22 100644 (file)
         settings.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>AmbientCapabilities=</varname></term>
+
+        <listitem><para>Controls which capabilities to include in the
+        ambient capability set for the executed process. Takes a
+        whitespace-separated list of capability names as read by
+        <citerefentry project='mankier'><refentrytitle>cap_from_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+        e.g. <constant>CAP_SYS_ADMIN</constant>,
+        <constant>CAP_DAC_OVERRIDE</constant>,
+        <constant>CAP_SYS_PTRACE</constant>. This option may appear more than
+        once in which case the ambient capability sets are merged.
+        If the list of capabilities is prefixed with <literal>~</literal>, all
+        but the listed capabilities will be included, the effect of the
+        assignment inverted. If the empty string is
+        assigned to this option, the ambient capability set is reset to
+        the empty capability set, and all prior settings have no effect.
+        If set to <literal>~</literal> (without any further argument), the
+        ambient capability set is reset to the full set of available
+        capabilities, also undoing any previous settings. Note that adding
+        capabilities to ambient capability set adds them to the process's
+        inherited capability set.
+        </para><para>
+        Ambient capability sets are useful if you want to execute a process
+        as a non-privileged user but still want to give it some capabilities.
+        Note that in this case option <constant>keep-caps</constant> is
+        automatically added to <varname>SecureBits=</varname> to retain the
+        capabilities over the user change.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>SecureBits=</varname></term>
         <listitem><para>Controls the secure bits set for the executed
index b36aab32598ad2e1edbbaef299f1cc34bc6ad94e..62658fb115f9e756cee22d6fdb113058600f361c 100644 (file)
     </example>
 
     <example>
-      <title>Debuging a generator</title>
+      <title>Debugging a generator</title>
 
       <programlisting>dir=$(mktemp -d)
 SYSTEMD_LOG_LEVEL=debug &systemgeneratordir;/systemd-fstab-generator \
index e6dedb027d11a80054ed2398e3a317cc326e43bf..5a6383cfc29de14616a358e2a55a5d475a050d55 100644 (file)
             <literal>ipv4</literal>, or <literal>ipv6</literal>.</para>
 
             <para>Note that DHCPv6 will by default be triggered by Router
-            Advertisment, if that is enabled, regardless of this parameter.
+            Advertisement, if that is enabled, regardless of this parameter.
             By enabling DHCPv6 support explicitly, the DHCPv6 client will
             be started regardless of the presence of routers on the link,
             or what flags the routers pass. See
         <varlistentry>
           <term><varname>LLMNR=</varname></term>
           <listitem>
-            <para>A boolean or <literal>resolve</literal>. When true, enables
-            Link-Local Multicast Name Resolution on the link. When set to
-            <literal>resolve</literal>, only resolution is enabled, but not
-            announcement. Defaults to true.</para>
+            <para>A boolean or <literal>resolve</literal>. When true,
+            enables <ulink
+            url="https://tools.ietf.org/html/rfc4795">Link-Local
+            Multicast Name Resolution</ulink> on the link. When set to
+            <literal>resolve</literal>, only resolution is enabled,
+            but not host registration and announcement. Defaults to
+            true. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>MulticastDNS=</varname></term>
+          <listitem>
+            <para>A boolean or <literal>resolve</literal>. When true,
+            enables <ulink
+            url="https://tools.ietf.org/html/rfc6762">Multicast
+            DNS</ulink> support on the link. When set to
+            <literal>resolve</literal>, only resolution is enabled,
+            but not host or service registration and
+            announcement. Defaults to false. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>DNSSEC=</varname></term>
+          <listitem>
+            <para>A boolean or
+            <literal>allow-downgrade</literal>. When true, enables
+            <ulink
+            url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
+            DNS validation support on the link. When set to
+            <literal>allow-downgrade</literal>, compatibility with
+            non-DNSSEC capable networks is increased, by automatically
+            turning off DNSEC in this case. This option defines a
+            per-interface setting for
+            <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
+            global <varname>DNSSEC=</varname> option. Defaults to
+            false. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>DNSSECNegativeTrustAnchors=</varname></term>
+          <listitem><para>A space-separated list of DNSSEC negative
+          trust anchor domains. If specified and DNSSEC is enabled,
+          look-ups done via the interface's DNS server will be subject
+          to the list of negative trust anchors, and not require
+          authentication for the specified domains, or anything below
+          it. Use this to disable DNSSEC authentication for specific
+          private domains, that cannot be proven valid using the
+          Internet DNS hierarchy. Defaults to the empty list. This
+          setting is read by
+          <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <para>A DNS server address, which must be in the format
             described in
             <citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-            This option may be specified more than once.</para>
+            This option may be specified more than once. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>Domains=</varname></term>
           <listitem>
-            <para>The domains used for DNS resolution over this link.</para>
+            <para>The domains used for DNS resolution over this link. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>NTP=</varname></term>
           <listitem>
-            <para>An NTP server address. This option may be specified more than once.</para>
+            <para>An NTP server address. This option may be specified more than once. This setting is read by
+            <citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>UseTimezone=</varname></term>
 
           <listitem><para>When true, the timezone received from the
-          DHCP server will be set as as timezone of the local
+          DHCP server will be set as timezone of the local
           system. Defaults to <literal>no</literal>.</para></listitem>
         </varlistentry>
 
@@ -1011,9 +1063,10 @@ DHCP=yes
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 54e7c49a9e4de676bece3ec9550ab9a3477b493c..d28f3d5f90d44aebc82cb7762f0b02bbcc0e244f 100644 (file)
       <varlistentry>
         <term><filename>umount.target</filename></term>
         <listitem>
-          <para>A special target unit that umounts all mount and
+          <para>A special target unit that unmounts all mount and
           automount points on system shutdown.</para>
 
           <para>Mounts that shall be unmounted on system shutdown
index cfa13015b035a46a26e5d644219e288365b02039..29e235e2dc61fb204afa1d944d58f1fe4d6c69a2 100644 (file)
         unloaded. Turning this off is particularly useful for
         transient timer units that shall disappear after they first
         elapse. Note that this setting has an effect on repeatedly
-        starting the a timer unit that only elapses once: if
+        starting a timer unit that only elapses once: if
         <varname>RemainAfterElapse=</varname> is on, it will not be
         started again, and is guaranteed to elapse only once. However,
         if <varname>RemainAfterLeapse=</varname> is off, it might be
index 5b12378eda67b2891c33493acb7ddbce7feb3013..98466591348f3a71c31be1ed8084ee6235f0c995 100644 (file)
 
         <para><varname>ConditionSecurity=</varname> may be used to
         check whether the given security module is enabled on the
-        system. Currently, the recognized values values are
+        system. Currently, the recognized values are
         <varname>selinux</varname>,
         <varname>apparmor</varname>,
         <varname>ima</varname>,
         <filename>/var</filename> on the next following boot. Units
         making use of this condition should order themselves before
         <citerefentry><refentrytitle>systemd-update-done.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-        to make sure they run before the stamp files's modification
+        to make sure they run before the stamp file's modification
         time gets reset indicating a completed update.</para>
 
         <para><varname>ConditionFirstBoot=</varname> takes a boolean
index 6de18f8294c6db374a645ba90269bd0b8fcffcc9..b8d91b89438c6e42517a084ae0941e7576eaa57d 100644 (file)
         <option>inherit</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--machine-id=</option></term>
+
+        <listitem><para>Override the machine-id set on the hard drive,
+        useful for network booting or for containers. May not be set
+        to all zeros.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
         script runlevel link farms.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$SYSTEMD_COLORS</varname></term>
+
+        <listitem><para>Controls whether colorized output should be generated.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$LISTEN_PID</varname></term>
         <term><varname>$LISTEN_FDS</varname></term>
         than once to set multiple variables.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>systemd.machine_id=</varname></term>
+
+        <listitem><para>Takes a 32 character hex value to be
+        used for setting the machine-id. Intended mostly for
+        network booting where the same machine-id is desired
+        for every boot.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>quiet</varname></term>
 
index 5bf1f2956bb30e53736113cf922545689d74518c..3b6b1e3f112f6f69f6539677b7f1430c3c2950e9 100644 (file)
           be removed and be replaced by the symlink. If the argument
           is omitted, symlinks to files with the same name residing in
           the directory <filename>/usr/share/factory/</filename> are
-          created.</para></listitem>
+          created. Note that permissions and ownership on symlinks
+          are ignored.</para></listitem>
         </varlistentry>
 
         <varlistentry>
           <command>systemd-tmpfiles</command> will automatically add
           the required base entries for user and group based on the
           access mode of the file, unless base entries already exist
-          or are explictly specified. The mask will be added if not
+          or are explicitly specified. The mask will be added if not
           specified explicitly or already present. Lines of this type
           accept shell-style globs in place of normal path names. This
           can be useful for allowing additional access to certain
index 21050623c57d8a31482a53b2fd2d1b7596c6b9ba..88fecbc506a30c48caf033fd81729e6f081c78cd 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -1,14 +1,14 @@
 # Hungarian translation of systemd
-# Copyright (C) 2015. Free Software Foundation, Inc.
+# Copyright (C) 2015, 2016. Free Software Foundation, Inc.
 # This file is distributed under the same license as the systemd package.
 #
-# Gabor Kelemen <kelemeng at gnome dot hu>, 2015.
+# Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-11-22 16:37+0100\n"
-"PO-Revision-Date: 2015-01-02 22:58+0100\n"
+"POT-Creation-Date: 2016-01-02 13:41+0100\n"
+"PO-Revision-Date: 2016-01-02 13:45+0100\n"
 "Last-Translator: Gabor Kelemen <kelemeng at ubuntu dot com>\n"
 "Language-Team: Hungarian <openscope at googlegroups dot com>\n"
 "Language: hu\n"
@@ -29,15 +29,13 @@ msgstr ""
 "Hitelesítés szükséges a bevitt jelmondat visszaküldéséhez a rendszernek."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
-#, fuzzy
 msgid "Manage system services or other units"
-msgstr "Rendszerszolgáltatások vagy -egységek kezelése"
+msgstr "Rendszerszolgáltatások vagy más egységek kezelése"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
-#, fuzzy
 msgid "Authentication is required to manage system services or other units."
 msgstr ""
-"Hitelesítés szükséges a rendszerszolgáltatások vagy -egységek kezeléséhez."
+"Hitelesítés szükséges a rendszerszolgáltatások vagy más egységek kezeléséhez."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
 msgid "Manage system service or unit files"
@@ -51,14 +49,16 @@ msgstr ""
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
 msgid "Set or unset system and service manager environment variables"
 msgstr ""
+"Rendszer- és szolgáltatáskezelő környezeti változóinak beállítása vagy "
+"törlése"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
-#, fuzzy
 msgid ""
 "Authentication is required to set or unset system and service manager "
 "environment variables."
 msgstr ""
-"Hitelesítés szükséges a rendszerszolgáltatás- vagy egységfájlok kezeléséhez."
+"Hitelesítés szükséges a rendszer- és szolgáltatáskezelő környezeti "
+"változóinak beállításához vagy törléséhez."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
 msgid "Reload the systemd state"
@@ -98,30 +98,27 @@ msgstr "Hitelesítés szükséges a helyi gép információinak beállításáho
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:1
 msgid "Import a VM or container image"
-msgstr ""
+msgstr "VM vagy konténer lemezkép importálása"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:2
-#, fuzzy
 msgid "Authentication is required to import a VM or container image"
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához."
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:3
 msgid "Export a VM or container image"
-msgstr ""
+msgstr "VM vagy konténer lemezkép exportálása"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:4
-#, fuzzy
 msgid "Authentication is required to export a VM or container image"
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép exportálásához."
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:5
 msgid "Download a VM or container image"
-msgstr ""
+msgstr "VM vagy konténer lemezkép letöltése"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:6
-#, fuzzy
 msgid "Authentication is required to download a VM or container image"
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez."
 
 #: ../src/locale/org.freedesktop.locale1.policy.in.h:1
 msgid "Set system locale"
@@ -409,123 +406,113 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
 msgid "Manage active sessions, users and seats"
-msgstr ""
+msgstr "Aktív munkamenetek, felhasználók és munkaállomások kezelése"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
-#, fuzzy
 msgid ""
 "Authentication is required for managing active sessions, users and seats."
 msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
+"kezeléséhez."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:51
 msgid "Lock or unlock active sessions"
-msgstr ""
+msgstr "Aktív munkamenetek zárolása vagy feloldása"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
-#, fuzzy
 msgid "Authentication is required to lock or unlock active sessions."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr ""
+"Hitelesítés szükséges az aktív munkamenetek zárolásához vagy feloldásához."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:53
 msgid "Allow indication to the firmware to boot to setup interface"
-msgstr ""
+msgstr "A firmware-nek jelezhető, hogy a beállítófelületet bootolja"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:54
-#, fuzzy
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
+"bootolja"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:55
 msgid "Set a wall message"
-msgstr ""
+msgstr "Falüzenet beállítása"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:56
-#, fuzzy
 msgid "Authentication is required to set a wall message"
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr "Hitelesítés szükséges a falüzenet beállításához"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
 msgstr "Bejelentkezés helyi konténerbe"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:2
-#, fuzzy
 msgid "Authentication is required to log into a local container."
 msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
-#, fuzzy
 msgid "Log into the local host"
-msgstr "Bejelentkezés helyi konténerbe"
+msgstr "Bejelentkezés a helyi gépre"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:4
-#, fuzzy
 msgid "Authentication is required to log into the local host."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a bejelentkezéshez a helyi gépre."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:5
-#, fuzzy
 msgid "Acquire a shell in a local container"
-msgstr "Bejelentkezés helyi konténerbe"
+msgstr "Parancsértelmező elérése helyi konténerben"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:6
-#, fuzzy
 msgid "Authentication is required to acquire a shell in a local container."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a parancsértelmező eléréséhez helyi konténerben."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:7
 msgid "Acquire a shell on the local host"
-msgstr ""
+msgstr "Parancsértelmező elérése a helyi gépen"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:8
-#, fuzzy
 msgid "Authentication is required to acquire a shell on the local host."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr "Hitelesítés szükséges a parancsértelmező eléréséhez a helyi gépen."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:9
-#, fuzzy
 msgid "Acquire a pseudo TTY in a local container"
-msgstr "Bejelentkezés helyi konténerbe"
+msgstr "Pszeudoterminál elérése helyi konténerben"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:10
-#, fuzzy
 msgid ""
 "Authentication is required to acquire a pseudo TTY in a local container."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a pszeudoterminál eléréséhez helyi konténerben."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:11
 msgid "Acquire a pseudo TTY on the local host"
-msgstr ""
+msgstr "Pszeudoterminál elérése helyi gépen"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:12
-#, fuzzy
 msgid "Authentication is required to acquire a pseudo TTY on the local host."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr "Hitelesítés szükséges a pszeudoterminál eléréséhez a helyi gépen."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
-msgstr ""
+msgstr "Virtuális gépek és konténerek kezelése"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:14
-#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
-msgstr "Hitelesítés szükséges a helyi gép információinak beállításához."
+msgstr "Hitelesítés szükséges helyi virtuális gépek és konténerek kezeléséhez."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
-msgstr ""
+msgstr "Helyi virtuális gép és konténer lemezképek kezelése"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:16
-#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
-msgstr "Hitelesítés szükséges a helyi gép információinak beállításához."
+msgstr ""
+"Hitelesítés szükséges a helyi virtuális gép és konténer lemezképek "
+"kezeléséhez."
 
 #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
 msgid "Set system time"
@@ -565,37 +552,33 @@ msgid ""
 "shall be enabled."
 msgstr "Hitelesítés szükséges a hálózati időszinkronizáció engedélyezéséhez."
 
-#: ../src/core/dbus-unit.c:428
-#, fuzzy
+#: ../src/core/dbus-unit.c:449
 msgid "Authentication is required to start '$(unit)'."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a következő elindításához: „$(unit)”."
 
-#: ../src/core/dbus-unit.c:429
-#, fuzzy
+#: ../src/core/dbus-unit.c:450
 msgid "Authentication is required to stop '$(unit)'."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a következő leállításához: „$(unit)”."
 
-#: ../src/core/dbus-unit.c:430
-#, fuzzy
+#: ../src/core/dbus-unit.c:451
 msgid "Authentication is required to reload '$(unit)'."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a következő újratöltéséhez: „$(unit)”."
 
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
-#, fuzzy
+#: ../src/core/dbus-unit.c:452 ../src/core/dbus-unit.c:453
 msgid "Authentication is required to restart '$(unit)'."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a következő újraindításához: „$(unit)”."
 
-#: ../src/core/dbus-unit.c:535
-#, fuzzy
+#: ../src/core/dbus-unit.c:556
 msgid "Authentication is required to kill '$(unit)'."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a következő kilövéséhez: „$(unit)”."
 
-#: ../src/core/dbus-unit.c:565
-#, fuzzy
+#: ../src/core/dbus-unit.c:586
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
+"„$(unit)”."
 
-#: ../src/core/dbus-unit.c:597
-#, fuzzy
+#: ../src/core/dbus-unit.c:618
 msgid "Authentication is required to set properties on '$(unit)'."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr ""
+"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
index e93a8c0ecb02af6845b486a8d812984a6892462c..66044b8686bd27926b386daa1b9a90cf7e256893 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -2,21 +2,21 @@
 # Copyright (C) 2014 systemd's COPYRIGHT HOLDER
 # This file is distributed under the same license as the systemd package.
 # Eugene Melnik <jeka7js@gmail.com>, 2014.
-# Daniel Korostil <ted.korostiled@gmail.com>, 2014.
+# Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-11-22 16:37+0100\n"
-"PO-Revision-Date: 2014-07-16 19:13+0300\n"
+"POT-Creation-Date: 2016-01-11 09:21+0200\n"
+"PO-Revision-Date: 2016-01-11 11:00+0300\n"
 "Last-Translator: Daniel Korostil <ted.korostiled@gmail.com>\n"
 "Language-Team: linux.org.ua\n"
 "Language: uk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 "X-Generator: Virtaal 0.7.1\n"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
@@ -30,43 +30,42 @@ msgstr "Засвідчення потрібно, щоб надіслати вв
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
 msgid "Manage system services or other units"
-msgstr ""
+msgstr "Керувати системними службами й іншими одиницями"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
-#, fuzzy
 msgid "Authentication is required to manage system services or other units."
-msgstr "Засвідчення потрібно, щоб доступитись до менеджера системи і служб."
+msgstr ""
+"Засвідчення потрібно, щоб керувати системними службами й іншими одиницями."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
 msgid "Manage system service or unit files"
-msgstr ""
+msgstr "Керувати системними службами й файлами одиниць"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
-#, fuzzy
 msgid "Authentication is required to manage system service or unit files."
-msgstr "Засвідчення потрібно, щоб доступитись до менеджера системи і служб."
+msgstr ""
+"Засвідчення потрібно, щоб керувати системними службами й файлами одиниць."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
-#, fuzzy
 msgid "Set or unset system and service manager environment variables"
-msgstr "Привілейований доступ до менеджера системи і служб"
+msgstr ""
+"Встановити або забрати змінну середовища з керування службами і системою"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
-#, fuzzy
 msgid ""
 "Authentication is required to set or unset system and service manager "
 "environment variables."
-msgstr "Засвідчення потрібно, щоб доступитись до менеджера системи і служб."
+msgstr ""
+"Засвідчення потрібно, щоб установити або забрати змінні середовища з "
+"керування службами і системою."
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
-#, fuzzy
 msgid "Reload the systemd state"
-msgstr "Ð\9fеÑ\80езаванÑ\82ажиÑ\82и Ñ\81иÑ\81Ñ\82емÑ\83"
+msgstr "Ð\9fеÑ\80езапÑ\83Ñ\81Ñ\82иÑ\82и Ñ\81Ñ\82ан Ñ\81иÑ\81Ñ\82еми"
 
 #: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10
-#, fuzzy
 msgid "Authentication is required to reload the systemd state."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ñ\81иÑ\81Ñ\82емний Ñ\87аÑ\81."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð¿ÐµÑ\80езапÑ\83Ñ\81Ñ\82иÑ\82и Ñ\81Ñ\82ан Ñ\81иÑ\81Ñ\82еми."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
 msgid "Set host name"
@@ -98,30 +97,31 @@ msgstr "Засвідчення потрібно, щоб вказати лока
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:1
 msgid "Import a VM or container image"
-msgstr ""
+msgstr "Імпортувати образ контейнера або віртуальної машини"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:2
-#, fuzzy
 msgid "Authentication is required to import a VM or container image"
-msgstr "Засвідчення потрібно, щоб вказати системний час."
+msgstr ""
+"Засвідчення потрібно, щоб імпортувати образ контейнера або віртуальної машини"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:3
 msgid "Export a VM or container image"
-msgstr ""
+msgstr "Експортувати образ контейнера або віртуальної машини"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:4
-#, fuzzy
 msgid "Authentication is required to export a VM or container image"
-msgstr "Засвідчення потрібно, щоб вказати системний час."
+msgstr ""
+"Засвідчення потрібно, щоб експортувати образ контейнера або віртуальної "
+"машини"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:5
 msgid "Download a VM or container image"
-msgstr ""
+msgstr "Звантажити образ контейнера або віртуальної машини"
 
 #: ../src/import/org.freedesktop.import1.policy.in.h:6
-#, fuzzy
 msgid "Authentication is required to download a VM or container image"
-msgstr "Засвідчення потрібно, щоб вказати локальну інформацію про машини."
+msgstr ""
+"Засвідчення потрібно, щоб звантажити образ контейнера або віртуальної машини"
 
 #: ../src/locale/org.freedesktop.locale1.policy.in.h:1
 msgid "Set system locale"
@@ -277,7 +277,7 @@ msgstr "Засвідчення потрібно, щоб вимкнути сис
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
 msgid "Power off the system while other users are logged in"
-msgstr "Ð\92имикнÑ\83Ñ\82и Ñ\81иÑ\81Ñ\82емÑ\83, ÐºÐ¾Ð»Ð¸ Ñ\96нÑ\88Ñ\96 ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87Ñ\96 Ñ\89е Ð² Ð½Ñ\96й"
+msgstr "Вимкнути систему, коли інші користувачі ще в ній"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
@@ -288,7 +288,7 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
 msgid "Power off the system while an application asked to inhibit it"
-msgstr "Вимкнути систему, коли програми намагаються першкодити цьому"
+msgstr "Вимкнути систему, коли програми намагаються перешкодити цьому"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
@@ -296,7 +296,7 @@ msgid ""
 "asked to inhibit it."
 msgstr ""
 "Засвідчення потрібно, щоб вимкнути систему, коли програми намагаються "
-"першкодити цьому."
+"перешкодити цьому."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
 msgid "Reboot the system"
@@ -308,7 +308,7 @@ msgstr "Для перезавантаження системи необхідн
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
 msgid "Reboot the system while other users are logged in"
-msgstr "Ð\9fеÑ\80езаванÑ\82ажиÑ\82и, Ñ\8fкÑ\89о Ñ\96нÑ\89і користувачі в системі"
+msgstr "Ð\9fеÑ\80езаванÑ\82ажиÑ\82и, Ñ\8fкÑ\89о Ñ\96нÑ\88і користувачі в системі"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
@@ -319,7 +319,7 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
 msgid "Reboot the system while an application asked to inhibit it"
-msgstr "Перезапустити систему, коли програми намагаються першкодити цьому"
+msgstr "Перезапустити систему, коли програми намагаються перешкодити цьому"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
@@ -327,7 +327,7 @@ msgid ""
 "asked to inhibit it."
 msgstr ""
 "Засвідчення потрібно, щоб перезапустити систему, коли програми намагаються "
-"першкодити цьому."
+"перешкодити цьому."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
 msgid "Suspend the system"
@@ -350,15 +350,15 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
 msgid "Suspend the system while an application asked to inhibit it"
-msgstr "Призупинити систему, коли програми намагаються першкодити цьому"
+msgstr "Призупинити систему, коли програми намагаються перешкодити цьому"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
 "Authentication is required for suspending the system while an application "
 "asked to inhibit it."
 msgstr ""
-"Засвідчення потрібно, щоб призупнити систему, коли програми намагаються "
-"першкодити цьому."
+"Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð¿Ñ\80изÑ\83пиниÑ\82и Ñ\81иÑ\81Ñ\82емÑ\83, ÐºÐ¾Ð»Ð¸ Ð¿Ñ\80огÑ\80ами Ð½Ð°Ð¼Ð°Ð³Ð°Ñ\8eÑ\82Ñ\8cÑ\81Ñ\8f "
+"перешкодити цьому."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
 msgid "Hibernate the system"
@@ -381,7 +381,7 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
 msgid "Hibernate the system while an application asked to inhibit it"
-msgstr "Приспати систему, коли програми намагаються першкодити цьому"
+msgstr "Приспати систему, коли програми намагаються перешкодити цьому"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
@@ -389,124 +389,118 @@ msgid ""
 "asked to inhibit it."
 msgstr ""
 "Засвідчення потрібно, щоб приспати систему, коли програми намагаються "
-"першкодити цьому."
+"перешкодити цьому."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
 msgid "Manage active sessions, users and seats"
-msgstr ""
+msgstr "Керувати сеансами, користувачами і робочими місцями"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
-#, fuzzy
 msgid ""
 "Authentication is required for managing active sessions, users and seats."
-msgstr "Засвідчення потрібно, щоб під'єднувати пристрої до місць."
+msgstr ""
+"Засвідчення потрібно, щоб керувати сеансами, користувачами і робочими "
+"місцями."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:51
 msgid "Lock or unlock active sessions"
-msgstr ""
+msgstr "Заблокувати або розблокувати сеанси"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
-#, fuzzy
 msgid "Authentication is required to lock or unlock active sessions."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ð»Ð¾ÐºÐ°Ð»Ñ\8cнÑ\83 Ñ\96нÑ\84оÑ\80маÑ\86Ñ\96Ñ\8e Ð¿Ñ\80о Ð¼Ð°Ñ\88ини."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð·Ð°Ð±Ð»Ð¾ÐºÑ\83ваÑ\82и Ð°Ð±Ð¾ Ñ\80озблокÑ\83ваÑ\82и Ñ\81еанÑ\81и."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:53
 msgid "Allow indication to the firmware to boot to setup interface"
-msgstr ""
+msgstr "Дозволити мікрокоду визначати, чи завантажувати інтерфейс встановлення"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:54
-#, fuzzy
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr ""
+"Засвідчення потрібне, щоб дозволити мікрокоду визначати, чи завантажувати "
+"інтерфейс встановлення."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:55
 msgid "Set a wall message"
-msgstr ""
+msgstr "Вказати повідомлення на стіні"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:56
-#, fuzzy
 msgid "Authentication is required to set a wall message"
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr "Засвідчення потрібне, щоб вказати повідомлення на стіні"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:1
 msgid "Log into a local container"
-msgstr ""
+msgstr "Увійти в локальний контейнер"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:2
-#, fuzzy
 msgid "Authentication is required to log into a local container."
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr "Засвідчення потрібне, щоб увійти в локальний контейнер."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:3
 msgid "Log into the local host"
-msgstr ""
+msgstr "Увійти в локальний вузол"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:4
-#, fuzzy
 msgid "Authentication is required to log into the local host."
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr "Засвідчення потрібне, щоб увійти в локальний вузол."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:5
-#, fuzzy
 msgid "Acquire a shell in a local container"
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\9fеÑ\80ейнÑ\8fÑ\82и Ð¾Ð±Ð¾Ð»Ð¾Ð½ÐºÑ\83 Ð² Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 ÐºÐ¾Ð½Ñ\82ейнеÑ\80Ñ\96"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:6
-#, fuzzy
 msgid "Authentication is required to acquire a shell in a local container."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð¿ÐµÑ\80ейнÑ\8fÑ\82и Ð¾Ð±Ð¾Ð»Ð¾Ð½ÐºÑ\83 Ð² Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 ÐºÐ¾Ð½Ñ\82ейнеÑ\80Ñ\96."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:7
 msgid "Acquire a shell on the local host"
-msgstr ""
+msgstr "Перейняти оболонку на локальному вузлі"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:8
-#, fuzzy
 msgid "Authentication is required to acquire a shell on the local host."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð¿ÐµÑ\80ейнÑ\8fÑ\82и Ð¾Ð±Ð¾Ð»Ð¾Ð½ÐºÑ\83 Ð½Ð° Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 Ð²Ñ\83злÑ\96."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:9
-#, fuzzy
 msgid "Acquire a pseudo TTY in a local container"
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\9fеÑ\80ейнÑ\8fÑ\82и Ð¿Ñ\81евдо TTY Ð² Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 ÐºÐ¾Ð½Ñ\82ейнеÑ\80Ñ\96"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:10
-#, fuzzy
 msgid ""
 "Authentication is required to acquire a pseudo TTY in a local container."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð¿ÐµÑ\80ейнÑ\8fÑ\82и Ð¿Ñ\81евдо TTY Ð² Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 ÐºÐ¾Ð½Ñ\82ейнеÑ\80Ñ\96."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:11
 msgid "Acquire a pseudo TTY on the local host"
-msgstr ""
+msgstr "Перейняти псевдо TTY на локальному вузлі"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:12
-#, fuzzy
 msgid "Authentication is required to acquire a pseudo TTY on the local host."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð²Ñ\81Ñ\82ановиÑ\82и Ð½Ð°Ð·Ð²Ñ\83 Ð»Ð¾ÐºÐ°Ð»Ñ\8cного Ð²Ñ\83зла."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бне, Ñ\89об Ð¿ÐµÑ\80ейнÑ\8fÑ\82и Ð¿Ñ\81евдо TTY Ð½Ð° Ð»Ð¾ÐºÐ°Ð»Ñ\8cномÑ\83 Ð²Ñ\83злÑ\96."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:13
 msgid "Manage local virtual machines and containers"
-msgstr ""
+msgstr "Керувати локальними віртуальними машинами і контейнерами"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:14
-#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
-msgstr "Засвідчення потрібно, щоб вказати локальну інформацію про машини."
+msgstr ""
+"Засвідчення потрібно, щоб керувати локальними віртуальними машинами і "
+"контейнерами."
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:15
 msgid "Manage local virtual machine and container images"
-msgstr ""
+msgstr "Керувати локальними образами віртуальних машин і контейнерів"
 
 #: ../src/machine/org.freedesktop.machine1.policy.in.h:16
-#, fuzzy
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
-msgstr "Засвідчення потрібно, щоб вказати локальну інформацію про машини."
+msgstr ""
+"Засвідчення потрібно, щоб керувати локальними образами віртуальних машин і "
+"контейнерів."
 
 #: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
 msgid "Set system time"
@@ -546,37 +540,30 @@ msgstr ""
 "Засвідчення потрібно, щоб контролювати, чи синхронізування часу через мережу "
 "запущено."
 
-#: ../src/core/dbus-unit.c:428
-#, fuzzy
+#: ../src/core/dbus-unit.c:449
 msgid "Authentication is required to start '$(unit)'."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ñ\81иÑ\81Ñ\82емний Ñ\87аÑ\81."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð·Ð°Ð¿Ñ\83Ñ\81Ñ\82иÑ\82и Â«$(unit)»."
 
-#: ../src/core/dbus-unit.c:429
-#, fuzzy
+#: ../src/core/dbus-unit.c:450
 msgid "Authentication is required to stop '$(unit)'."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ñ\81иÑ\81Ñ\82емний Ñ\87аÑ\81."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð·Ñ\83пиниÑ\82и Â«$(unit)»."
 
-#: ../src/core/dbus-unit.c:430
-#, fuzzy
+#: ../src/core/dbus-unit.c:451
 msgid "Authentication is required to reload '$(unit)'."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ñ\81иÑ\81Ñ\82емний Ñ\87аÑ\81."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð¿ÐµÑ\80езаванÑ\82ажиÑ\82и Â«$(unit)»."
 
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
-#, fuzzy
+#: ../src/core/dbus-unit.c:452 ../src/core/dbus-unit.c:453
 msgid "Authentication is required to restart '$(unit)'."
-msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð²ÐºÐ°Ð·Ð°Ñ\82и Ñ\81иÑ\81Ñ\82емний Ñ\87аÑ\81."
+msgstr "Ð\97аÑ\81вÑ\96дÑ\87еннÑ\8f Ð¿Ð¾Ñ\82Ñ\80Ñ\96бно, Ñ\89об Ð¿ÐµÑ\80езапÑ\83Ñ\81Ñ\82иÑ\82и Â«$(unit)»."
 
-#: ../src/core/dbus-unit.c:535
-#, fuzzy
+#: ../src/core/dbus-unit.c:556
 msgid "Authentication is required to kill '$(unit)'."
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr "Засвідчення потрібне, щоб вбити «$(unit)»."
 
-#: ../src/core/dbus-unit.c:565
-#, fuzzy
+#: ../src/core/dbus-unit.c:586
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
-msgstr "Засвідчення потрібне, щоб встановити назву локального вузла."
+msgstr "Засвідчення потрібне, щоб скинути «пошкоджений» стан з «$(unit)»."
 
-#: ../src/core/dbus-unit.c:597
-#, fuzzy
+#: ../src/core/dbus-unit.c:618
 msgid "Authentication is required to set properties on '$(unit)'."
-msgstr "Засвідчення потрібно, щоб вказати системний час."
+msgstr "Засвідчення потрібно, щоб вказати властивості на «$(unit)»."
index 6e460697db1c9d73a06563180495abd35496f98b..3c9766da04f080e03baebbd9d12a0b77f5213cd8 100644 (file)
 #include "pager.h"
 #include "path-util.h"
 #include "strv.h"
+#include "unit-name.h"
+
+static int prepare_filename(const char *filename, char **ret) {
+        int r;
+        const char *name;
+        _cleanup_free_ char *abspath = NULL;
+        _cleanup_free_ char *dir = NULL;
+        _cleanup_free_ char *with_instance = NULL;
+        char *c;
+
+        assert(filename);
+        assert(ret);
+
+        r = path_make_absolute_cwd(filename, &abspath);
+        if (r < 0)
+                return r;
+
+        name = basename(abspath);
+        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
+                return -EINVAL;
+
+        if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
+                r = unit_name_replace_instance(name, "i", &with_instance);
+                if (r < 0)
+                        return r;
+        }
+
+        dir = dirname_malloc(abspath);
+        if (!dir)
+                return -ENOMEM;
+
+        if (with_instance)
+                c = path_join(NULL, dir, with_instance);
+        else
+                c = path_join(NULL, dir, name);
+        if (!c)
+                return -ENOMEM;
+
+        *ret = c;
+        return 0;
+}
 
 static int generate_path(char **var, char **filenames) {
         char **filename;
@@ -233,18 +274,19 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man)
         log_debug("Loading remaining units from the command line...");
 
         STRV_FOREACH(filename, filenames) {
-                char fname[UNIT_NAME_MAX + 2 + 1] = "./";
+                _cleanup_free_ char *prepared = NULL;
 
                 log_debug("Handling %s...", *filename);
 
-                /* manager_load_unit does not like pure basenames, so prepend
-                 * the local directory, but only for valid names. manager_load_unit
-                 * will print the error for other ones. */
-                if (!strchr(*filename, '/') && strlen(*filename) <= UNIT_NAME_MAX) {
-                        strncat(fname + 2, *filename, UNIT_NAME_MAX);
-                        k = manager_load_unit(m, NULL, fname, &err, &units[count]);
-                } else
-                        k = manager_load_unit(m, NULL, *filename, &err, &units[count]);
+                k = prepare_filename(*filename, &prepared);
+                if (k < 0) {
+                        log_error_errno(k, "Failed to prepare filename %s: %m", *filename);
+                        if (r == 0)
+                                r = k;
+                        continue;
+                }
+
+                k = manager_load_unit(m, NULL, prepared, &err, &units[count]);
                 if (k < 0) {
                         log_error_errno(k, "Failed to load %s: %m", *filename);
                         if (r == 0)
index b0fa079fec2b560b0979d43704a1c4e8de1ce39b..a59459bc26b3ab6ff91ff3e9cc723e120107963c 100644 (file)
@@ -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();
index 95f59e400a4e13bae9ea9bb86729324106269725..50078822a7641e8c9f1e5d91218d25cede33dcc5 100644 (file)
@@ -140,7 +140,8 @@ bool bitmap_isset(Bitmap *b, unsigned n) {
 bool bitmap_isclear(Bitmap *b) {
         unsigned i;
 
-        assert(b);
+        if (!b)
+                return true;
 
         for (i = 0; i < b->n_bitmaps; i++)
                 if (b->bitmaps[i] != 0)
@@ -150,7 +151,9 @@ bool bitmap_isclear(Bitmap *b) {
 }
 
 void bitmap_clear(Bitmap *b) {
-        assert(b);
+
+        if (!b)
+                return;
 
         b->bitmaps = mfree(b->bitmaps);
         b->n_bitmaps = 0;
@@ -197,7 +200,10 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) {
         Bitmap *c;
         unsigned i;
 
-        if (!a ^ !b)
+        if (a == b)
+                return true;
+
+        if (!a != !b)
                 return false;
 
         if (!a)
index acd48f6954a7522d31888c60b91409288e368fd6..d07d1df5a8872d7d024dc849fa4b76cd4963d42b 100644 (file)
@@ -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;
index fef722b6f2bdd20fc2abfbac1e35d81cb5ed0cbb..49c2d61afe71250771e002539fea094fe6dff49a 100644 (file)
@@ -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");
 
index 6bbf7318fd310e3788a6a2de6d1af8e3674c214b..be41475441a99bc89e797ed0a83c15b17525aede 100644 (file)
 #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;
+}
index 639f9f3db1c75b81a5cf38ff4ac560838972a774..f873fb89d36d73e93c259bdabe7cc54a0c23b8a9 100644 (file)
@@ -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;
index 00f549c023fc9b640aabba36519f4740b398632e..05788a360e3739de14b6a1841bf0cfb347dc6972 100644 (file)
@@ -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;
index 75dad228e394574bdfc0964a132371adb7267fd9..5854caeb518d1a2ec49b07520c73a0ebd757688d 100644 (file)
@@ -41,6 +41,7 @@
 static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
         _cleanup_closedir_ DIR *dir = NULL;
         const char *dirpath;
+        struct dirent *de;
         int r;
 
         assert(path);
@@ -55,18 +56,9 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
                 return -errno;
         }
 
-        for (;;) {
-                struct dirent *de;
+        FOREACH_DIRENT(de, dir, return -errno) {
                 char *p;
 
-                errno = 0;
-                de = readdir(dir);
-                if (!de && errno != 0)
-                        return -errno;
-
-                if (!de)
-                        break;
-
                 if (!dirent_is_file_with_suffix(de, suffix))
                         continue;
 
@@ -116,17 +108,15 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
 
         STRV_FOREACH(p, dirs) {
                 r = files_add(fh, root, *p, suffix);
-                if (r == -ENOMEM) {
+                if (r == -ENOMEM)
                         return r;
-                } else if (r < 0)
-                        log_debug_errno(r, "Failed to search for files in %s: %m",
-                                        *p);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p);
         }
 
         files = hashmap_get_strv(fh);
-        if (files == NULL) {
+        if (!files)
                 return -ENOMEM;
-        }
 
         qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp);
         *strv = files;
index 0a66902ac9029a36342bb5f110726f4cc7e90fb6..b4d080103b5f3d13874d12d4b709bed4494df21e 100644 (file)
@@ -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;
 }
 
index ab282efa3c48cf27dbe10271b3addba935ea03f9..5661f3681376ce1eaa0811198d71d01fa506fd08 100644 (file)
@@ -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;
index c710f017432a88389afcb7e69cf41293349e263d..d943aa71f5b005e39bb68c2db6058bf5808e1d0c 100644 (file)
@@ -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);
 
index 7cc2a1de136761ac013cc661884095aee86440d6..090d2a7884cab89d64fd7e102101e96e58823bdd 100644 (file)
@@ -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;
index 5ce1592eeb414afaf24caee3b48e104006bb18e5..973413ff42fd029f04817c9c8a47420725d4ceaa 100644 (file)
@@ -73,3 +73,6 @@ int same_fd(int a, int b);
 void cmsg_close_all(struct msghdr *mh);
 
 bool fdname_is_valid(const char *s);
+
+#define ERRNO_IS_DISCONNECT(r) \
+        IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)
index 3a237252b5ffe2e02663a4d3fc476f4bd29e9eaa..5ed5460904b4207d960ac4b11d7e94eca6997a99 100644 (file)
@@ -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;
 }
index fb760abe1816bb5f8b81161efe6da221eea429af..d31bd6e273fe8d881e2d1627ffebd2764db6bec1 100644 (file)
@@ -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;
index a0be0efd407e7e5f95485f16c1a45cdd6886ed96..811ab6ec36d204c2145a5c08719debb94fd4d490 100644 (file)
@@ -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/hash-funcs.c b/src/basic/hash-funcs.c
new file mode 100644 (file)
index 0000000..d4affaf
--- /dev/null
@@ -0,0 +1,83 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 "hash-funcs.h"
+
+void string_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, strlen(p) + 1, state);
+}
+
+int string_compare_func(const void *a, const void *b) {
+        return strcmp(a, b);
+}
+
+const struct hash_ops string_hash_ops = {
+        .hash = string_hash_func,
+        .compare = string_compare_func
+};
+
+void trivial_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(&p, sizeof(p), state);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops trivial_hash_ops = {
+        .hash = trivial_hash_func,
+        .compare = trivial_compare_func
+};
+
+void uint64_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, sizeof(uint64_t), state);
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+        uint64_t a, b;
+        a = *(const uint64_t*) _a;
+        b = *(const uint64_t*) _b;
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+        .hash = uint64_hash_func,
+        .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) {
+        siphash24_compress(p, sizeof(dev_t), state);
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+        dev_t a, b;
+        a = *(const dev_t*) _a;
+        b = *(const dev_t*) _b;
+        return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+        .hash = devt_hash_func,
+        .compare = devt_compare_func
+};
+#endif
diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h
new file mode 100644 (file)
index 0000000..c640eaf
--- /dev/null
@@ -0,0 +1,67 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2014 Michal Schmidt
+
+  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 "macro.h"
+#include "siphash24.h"
+
+typedef void (*hash_func_t)(const void *p, struct siphash *state);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+struct hash_ops {
+        hash_func_t hash;
+        compare_func_t compare;
+};
+
+void string_hash_func(const void *p, struct siphash *state);
+int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+void trivial_hash_func(const void *p, struct siphash *state);
+int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
+
+/* 32bit values we can always just embed in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+void uint64_hash_func(const void *p, struct siphash *state);
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+        .hash = devt_hash_func,
+        .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
index 286ddfef5b38024d201460469c90601dfa05d1ce..dcd8ae412d1b9013f4e1676f979934deb3a1caf1 100644 (file)
@@ -280,66 +280,6 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
         },
 };
 
-void string_hash_func(const void *p, struct siphash *state) {
-        siphash24_compress(p, strlen(p) + 1, state);
-}
-
-int string_compare_func(const void *a, const void *b) {
-        return strcmp(a, b);
-}
-
-const struct hash_ops string_hash_ops = {
-        .hash = string_hash_func,
-        .compare = string_compare_func
-};
-
-void trivial_hash_func(const void *p, struct siphash *state) {
-        siphash24_compress(&p, sizeof(p), state);
-}
-
-int trivial_compare_func(const void *a, const void *b) {
-        return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops trivial_hash_ops = {
-        .hash = trivial_hash_func,
-        .compare = trivial_compare_func
-};
-
-void uint64_hash_func(const void *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(uint64_t), state);
-}
-
-int uint64_compare_func(const void *_a, const void *_b) {
-        uint64_t a, b;
-        a = *(const uint64_t*) _a;
-        b = *(const uint64_t*) _b;
-        return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops uint64_hash_ops = {
-        .hash = uint64_hash_func,
-        .compare = uint64_compare_func
-};
-
-#if SIZEOF_DEV_T != 8
-void devt_hash_func(const void *p, struct siphash *state) {
-        siphash24_compress(p, sizeof(dev_t), state);
-}
-
-int devt_compare_func(const void *_a, const void *_b) {
-        dev_t a, b;
-        a = *(const dev_t*) _a;
-        b = *(const dev_t*) _b;
-        return a < b ? -1 : (a > b ? 1 : 0);
-}
-
-const struct hash_ops devt_hash_ops = {
-        .hash = devt_hash_func,
-        .compare = devt_compare_func
-};
-#endif
-
 static unsigned n_buckets(HashmapBase *h) {
         return h->has_indirect ? h->indirect.n_buckets
                                : hashmap_type_info[h->type].n_direct_buckets;
index 708811124b0c0c64728a0435bd10993bf5eb84cb..fdba9c61fff7eb5c1120f55bb31219a0fa1e890d 100644 (file)
@@ -26,8 +26,8 @@
 #include <stdbool.h>
 #include <stddef.h>
 
+#include "hash-funcs.h"
 #include "macro.h"
-#include "siphash24.h"
 #include "util.h"
 
 /*
@@ -70,47 +70,6 @@ typedef struct {
 #define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
 #define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
 
-typedef void (*hash_func_t)(const void *p, struct siphash *state);
-typedef int (*compare_func_t)(const void *a, const void *b);
-
-struct hash_ops {
-        hash_func_t hash;
-        compare_func_t compare;
-};
-
-void string_hash_func(const void *p, struct siphash *state);
-int string_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops string_hash_ops;
-
-/* This will compare the passed pointers directly, and will not
- * dereference them. This is hence not useful for strings or
- * suchlike. */
-void trivial_hash_func(const void *p, struct siphash *state);
-int trivial_compare_func(const void *a, const void *b) _const_;
-extern const struct hash_ops trivial_hash_ops;
-
-/* 32bit values we can always just embedd in the pointer itself, but
- * in order to support 32bit archs we need store 64bit values
- * indirectly, since they don't fit in a pointer. */
-void uint64_hash_func(const void *p, struct siphash *state);
-int uint64_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops uint64_hash_ops;
-
-/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
- * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
-#if SIZEOF_DEV_T != 8
-void devt_hash_func(const void *p, struct siphash *state) _pure_;
-int devt_compare_func(const void *a, const void *b) _pure_;
-extern const struct hash_ops devt_hash_ops = {
-        .hash = devt_hash_func,
-        .compare = devt_compare_func
-};
-#else
-#define devt_hash_func uint64_hash_func
-#define devt_compare_func uint64_compare_func
-#define devt_hash_ops uint64_hash_ops
-#endif
-
 /* Macros for type checking */
 #define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
         (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
index 5143dddf8f4e7db5c6b98a8d904ee19c5cd6dbaf..8609ffb3c99998ac4be1a1420e9ef35312e2057e 100644 (file)
@@ -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;
 }
index bcc116c783a8d1f6cae96b0ae0d5d3c017939ac7..f2b8865df5b03b5bc73f019e7eb7dd265e948ccb 100644 (file)
@@ -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);
index 1a9e6bdb91ebc446ca4c6bd803ef83cef4e92f2a..a2bc0d5be2db3ca31f3e0b48c0660170954fb4cb 100644 (file)
@@ -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;
index 5088e6720d8640e8e13c1d58e7c74a1da7ff9423..c529c6ecade118cd4a83552251930f28febd1d28 100644 (file)
@@ -320,18 +320,47 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
 #define SET_FLAG(v, flag, b) \
         (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
 
-#define IN_SET(x, y, ...)                                               \
-        ({                                                              \
-                static const typeof(y) _array[] = { (y), __VA_ARGS__ }; \
-                const typeof(y) _x = (x);                               \
-                unsigned _i;                                            \
-                bool _found = false;                                    \
-                for (_i = 0; _i < ELEMENTSOF(_array); _i++)             \
-                        if (_array[_i] == _x) {                         \
-                                _found = true;                          \
-                                break;                                  \
-                        }                                               \
-                _found;                                                 \
+#define CASE_F(X) case X:
+#define CASE_F_1(CASE, X) CASE_F(X)
+#define CASE_F_2(CASE, X, ...)  CASE(X) CASE_F_1(CASE, __VA_ARGS__)
+#define CASE_F_3(CASE, X, ...)  CASE(X) CASE_F_2(CASE, __VA_ARGS__)
+#define CASE_F_4(CASE, X, ...)  CASE(X) CASE_F_3(CASE, __VA_ARGS__)
+#define CASE_F_5(CASE, X, ...)  CASE(X) CASE_F_4(CASE, __VA_ARGS__)
+#define CASE_F_6(CASE, X, ...)  CASE(X) CASE_F_5(CASE, __VA_ARGS__)
+#define CASE_F_7(CASE, X, ...)  CASE(X) CASE_F_6(CASE, __VA_ARGS__)
+#define CASE_F_8(CASE, X, ...)  CASE(X) CASE_F_7(CASE, __VA_ARGS__)
+#define CASE_F_9(CASE, X, ...)  CASE(X) CASE_F_8(CASE, __VA_ARGS__)
+#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__)
+#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__)
+#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__)
+#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__)
+#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__)
+#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__)
+#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__)
+#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__)
+#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__)
+#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__)
+#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__)
+
+#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
+#define FOR_EACH_MAKE_CASE(...) \
+        GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
+                               CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
+                   (CASE_F,__VA_ARGS__)
+
+#define IN_SET(x, ...)                          \
+        ({                                      \
+                bool _found = false;            \
+                /* If the build breaks in the line below, you need to extend the case macros */ \
+                static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \
+                switch(x) {                     \
+                FOR_EACH_MAKE_CASE(__VA_ARGS__) \
+                        _found = true;          \
+                        break;                  \
+                default:                        \
+                        break;                  \
+                }                               \
+                _found;                         \
         })
 
 /* Define C11 thread_local attribute even on older gcc compiler
index d539ed00e4065e576ac67bc565df97c064580013..2d2785beadfc3354cec4b068dc649606a171fa35 100644 (file)
 #define NETLINK_LIST_MEMBERSHIPS 9
 #endif
 
+#ifndef SOL_SCTP
+#define SOL_SCTP 132
+#endif
+
 #if !HAVE_DECL_PIVOT_ROOT
 static inline int pivot_root(const char *new_root, const char *put_old) {
         return syscall(SYS_pivot_root, new_root, put_old);
@@ -1125,3 +1129,19 @@ 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
index 618ef5d56430bc02543f6b016ca48bca371b91d3..d8de6f90ea17833c860d9333009ea56f8872089c 100644 (file)
@@ -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;
         }
index 61fab0e0878a383d9992dc0a88d22bd181d72478..4837bb2d7d2fa71dde08ca21bef448a972d6810d 100644 (file)
@@ -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);
         }
index 14f8474da006bf8ef1ddd0ee60afae81cc2d6f58..4807561723b812e609ae4d8d801ed0f6382e2697 100644 (file)
@@ -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;
                 }
index 7637fccb2f137e9033fc6e7f7ce99240e025a6d4..315efadd93b101c07fc3ea37bc44586f452a3fa9 100644 (file)
@@ -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;
 }
index 3f7e20362b2ac6c837d1517ca7cf93e5fa9d264f..54e2420cc6e94beea881250c0050746387c1ea98 100644 (file)
@@ -16,6 +16,8 @@ struct siphash {
 
 void siphash24_init(struct siphash *state, const uint8_t k[16]);
 void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
+
 uint64_t siphash24_finalize(struct siphash *state);
 
 uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]);
index 2181a3a7676270df557fb1f3cce18e0547dd11fb..588404ab5a26ca5827f393d8176cbe27e4f94028 100644 (file)
@@ -47,16 +47,34 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                 return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
         }
 
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+        scope type name##_from_string(const char *s) {                  \
+                int b;                                                  \
+                b = parse_boolean(s);                                   \
+                if (b == 0)                                             \
+                        return (type) 0;                                \
+                else if (b > 0)                                         \
+                        return yes;                                     \
+                return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+        }
+
 #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope)                    \
         _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
         _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope)        \
         struct __useless_struct_to_allow_trailing_semicolon__
 
+#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope)   \
+        _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope)          \
+        _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
+        struct __useless_struct_to_allow_trailing_semicolon__
+
 #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
 #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
 
+#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
+
 /* For string conversions where numbers are also acceptable */
 #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max)         \
         int name##_to_string_alloc(type i, char **str) {                \
index 8178c7093fc04085ddeb74719e34d747c5ed1ce7..1f95a9abba2b033feba4dce67b2e7fb53dc99335 100644 (file)
@@ -317,18 +317,67 @@ char *truncate_nl(char *s) {
         return s;
 }
 
+char ascii_tolower(char x) {
+
+        if (x >= 'A' && x <= 'Z')
+                return x - 'A' + 'a';
+
+        return x;
+}
+
 char *ascii_strlower(char *t) {
         char *p;
 
         assert(t);
 
         for (p = t; *p; p++)
-                if (*p >= 'A' && *p <= 'Z')
-                        *p = *p - 'A' + 'a';
+                *p = ascii_tolower(*p);
 
         return t;
 }
 
+char *ascii_strlower_n(char *t, size_t n) {
+        size_t i;
+
+        if (n <= 0)
+                return t;
+
+        for (i = 0; i < n; i++)
+                t[i] = ascii_tolower(t[i]);
+
+        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;
 
index b59b9b5a710cff0f125b3c0cf705656993b1904d..8ea18f45aa6d74bed0be1d8a6547bc3d0b8e7bf5 100644 (file)
@@ -130,7 +130,12 @@ char *strstrip(char *s);
 char *delete_chars(char *s, const char *bad);
 char *truncate_nl(char *s);
 
-char *ascii_strlower(char *path);
+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_;
 
index a39764472b89f8e3fe9c42ed02a0dc72f4e64c3e..fedfc8a5dfdb166068c1f89313ff5e9eee01b203 100644 (file)
@@ -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;
+}
index 597a0060ad8a3f5028b054c72c20488c178c5b17..a7c96a77cbbbeff924944631644d4eb722db568e 100644 (file)
@@ -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);
index 56e1a3be48c4fc212968a71ee8286a9afc3ac55b..70a6e1f5e4b4114b09eb78c7a0e493d49b3d5601 100644 (file)
@@ -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)
index 9e0b576283b9f035c31b716165672fb14c1234b6..4434ecfdf6597e34af085682bd40b0176e2a011f 100644 (file)
@@ -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)
index 4cf42d17f34b76b6dc71e317990966297af888aa..77eea6aada40cac12f114f265292b9cfea098269 100644 (file)
@@ -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;
index 2bf473ffc18a2dcb4aed0133ce7834776fbbb5f8..79e261abe5d84d3cdcf2f4486c0957cf6f7a983d 100644 (file)
@@ -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)
index 0a5c11ad0c9f1ef998f223fb373b0c06f762ed31..4894296554697baeb62385d7194b04675963b3bb 100644 (file)
@@ -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)
index 1f736b26865d4c19773fb8badfc40cac32c3a8dd..eae0808f9e3c9dd4573e7fd97077feb26c964de9 100644 (file)
@@ -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) {
index 8a523cc8ac1e5902bbd0d747754ee1f0aa887577..c5c672a0a28ceadb893c310625ba3b65a2fb2e73 100644 (file)
@@ -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);
 }
index bcd4d1146bec44bb9a44c9ebf4ad75c26e0dba63..56ed9470895ffef47cd35a4de8a04955b2f79f9e 100644 (file)
@@ -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;
index 9b768619197420ab9d7fbc08bb25bbdec445c6e5..00287308891fec5f5d82172961bb9b10c868b42c 100644 (file)
@@ -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 */
@@ -1856,6 +1859,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,28 +1871,71 @@ 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) {
                         r = enforce_user(context, uid);
                         if (r < 0) {
                                 *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 +2162,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 +2319,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 +2566,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 +2683,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)));
index be5be9f531769a8b86b5d7768e829309e510cf0e..8649620830d6654662461bb29fcd6c70948896b2 100644 (file)
@@ -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;
index 9654590635dfc05488267f6109a59e90d0103857..274c554da91b0f43cda20f4ca65a97a0cac0db42 100644 (file)
@@ -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) {
index 3fa66f91aad61e464f062744a5e9fa7495b13284..569632e13b2ee36c2a599c6ac90d87b180d3408f 100644 (file)
@@ -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;
index 0408b9a82914b832bd8fc91bdb05d85533d63560..29ab1b6b9ed8d033d4fa278dc4274c51a1ad6a38 100644 (file)
@@ -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',
index cb553e1252d32410dd360ab85f66f81ae76409b3..d3880b4e3c4b21e5e3f71bcac43157ca7ec46663 100644 (file)
@@ -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"
@@ -1024,7 +1025,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,
@@ -1036,8 +1037,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;
 
@@ -1051,10 +1052,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 (;;) {
@@ -1073,18 +1073,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;
 }
@@ -4002,7 +4005,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 [...]]" },
index a451fc164a737d6e229e332ef4ec3022b1a085b6..f0027a6b438cf0eb3918ece854daa58b68fc9960 100644 (file)
@@ -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);
index 145ba2a28df62f98bf6c6334d419b746b64866e7..09b0449c80965763e014cb52780a60a5ebfbe04e 100644 (file)
@@ -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)
index f7707c3bf9f6d6d2959e0af88d222b289d37854c..a2168a8d4ae5ad2b95b6fe8c6e4942ef810df49a 100644 (file)
@@ -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);
index f9de54028e017daa6e8907b5354d19ec340e682b..2f9094f03a5c4c00c38a9b828c65917f8e5350be 100644 (file)
@@ -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";
index ffe27be7432fefa39f79bfa49639747f89f04ac5..a83a8b013af4f208cb4a51e621f602ca6de0bcce 100644 (file)
@@ -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;
@@ -380,6 +380,9 @@ static int enable_special_signals(Manager *m) {
 
         assert(m);
 
+        if (m->test_run)
+                return 0;
+
         /* Enable that we get SIGINT on control-alt-del. In containers
          * this will fail with EPERM (older) or EINVAL (newer), so
          * ignore that. */
@@ -986,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);
@@ -2920,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;
 
index 2b8d590ed1f94dbffbbc65df1e25f7e2b9f2c313..d73b319c5da6e883793781b8f96440e38888fc03 100644 (file)
@@ -304,13 +304,18 @@ int mount_cgroup_controllers(char ***join_controllers) {
                                         return log_oom();
 
                                 r = symlink(options, t);
-                                if (r < 0 && errno != EEXIST)
-                                        return log_error_errno(errno, "Failed to create symlink %s: %m", t);
+                                if (r >= 0) {
 #ifdef SMACK_RUN_LABEL
-                                r = mac_smack_copy(t, options);
-                                if (r < 0 && r != -EOPNOTSUPP)
-                                        return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", options, t);
+                                        _cleanup_free_ char *src;
+                                        src = strappend("/sys/fs/cgroup/", options);
+                                        if (!src)
+                                                return log_oom();
+                                        r = mac_smack_copy(t, src);
+                                        if (r < 0 && r != -EOPNOTSUPP)
+                                                return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
 #endif
+                                } else if (errno != EEXIST)
+                                        return log_error_errno(errno, "Failed to create symlink %s: %m", t);
                         }
                 }
         }
index 0661ff9ecd5214f95010c21c3d73d4b6727e1fca..c9374ca0e8237cc1627e8d7de47d0a42c2a3863e 100644 (file)
@@ -197,6 +197,75 @@ static int write_cipso2_rules(const char* srcdir) {
         return r;
 }
 
+static int write_netlabel_rules(const char* srcdir) {
+        _cleanup_fclose_ FILE *dst = NULL;
+        _cleanup_closedir_ DIR *dir = NULL;
+        struct dirent *entry;
+        char buf[NAME_MAX];
+        int dfd = -1;
+        int r = 0;
+
+        dst = fopen("/sys/fs/smackfs/netlabel", "we");
+        if (!dst)  {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to open /sys/fs/smackfs/netlabel: %m");
+                return -errno; /* negative error */
+        }
+
+        /* write rules to dst from every file in the directory */
+        dir = opendir(srcdir);
+        if (!dir) {
+                if (errno != ENOENT)
+                        log_warning_errno(errno, "Failed to opendir %s: %m", srcdir);
+                return errno; /* positive on purpose */
+        }
+
+        dfd = dirfd(dir);
+        assert(dfd >= 0);
+
+        FOREACH_DIRENT(entry, dir, return 0) {
+                int fd;
+                _cleanup_fclose_ FILE *policy = NULL;
+
+                fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+                if (fd < 0) {
+                        if (r == 0)
+                                r = -errno;
+                        log_warning_errno(errno, "Failed to open %s: %m", entry->d_name);
+                        continue;
+                }
+
+                policy = fdopen(fd, "re");
+                if (!policy) {
+                        if (r == 0)
+                                r = -errno;
+                        safe_close(fd);
+                        log_error_errno(errno, "Failed to open %s: %m", entry->d_name);
+                        continue;
+                }
+
+                /* load2 write rules in the kernel require a line buffered stream */
+                FOREACH_LINE(buf, policy,
+                             log_error_errno(errno, "Failed to read line from %s: %m",
+                                       entry->d_name)) {
+                        if (!fputs(buf, dst)) {
+                                if (r == 0)
+                                        r = -EINVAL;
+                                log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel");
+                                break;
+                        }
+                        if (fflush(dst)) {
+                                if (r == 0)
+                                        r = -errno;
+                                log_error_errno(errno, "Failed to flush writes to /sys/fs/smackfs/netlabel: %m");
+                                break;
+                        }
+                }
+        }
+
+       return r;
+}
+
 #endif
 
 int mac_smack_setup(bool *loaded_policy) {
@@ -225,8 +294,18 @@ int mac_smack_setup(bool *loaded_policy) {
 
 #ifdef SMACK_RUN_LABEL
         r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
-        if (r)
-                log_warning_errno(r, "Failed to set SMACK label \"%s\" on self: %m", SMACK_RUN_LABEL);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set SMACK label \"" SMACK_RUN_LABEL "\" on self: %m");
+        r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, 0);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set SMACK ambient label \"" SMACK_RUN_LABEL "\": %m");
+        r = write_string_file("/sys/fs/smackfs/netlabel",
+                              "0.0.0.0/0 " SMACK_RUN_LABEL, 0);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set SMACK netlabel rule \"0.0.0.0/0 " SMACK_RUN_LABEL "\": %m");
+        r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", 0);
+        if (r < 0)
+                log_warning_errno(r, "Failed to set SMACK netlabel rule \"127.0.0.1 -CIPSO\": %m");
 #endif
 
         r = write_cipso2_rules("/etc/smack/cipso.d/");
@@ -236,13 +315,29 @@ int mac_smack_setup(bool *loaded_policy) {
                 return 0;
         case ENOENT:
                 log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
-                return 0;
+                break;
         case 0:
                 log_info("Successfully loaded Smack/CIPSO policies.");
                 break;
         default:
                 log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m");
+                break;
+        }
+
+        r = write_netlabel_rules("/etc/smack/netlabel.d/");
+        switch(r) {
+        case -ENOENT:
+                log_debug("Smack/CIPSO is not enabled in the kernel.");
                 return 0;
+        case ENOENT:
+                log_debug("Smack network host rules directory '/etc/smack/netlabel.d/' not found");
+                break;
+        case 0:
+                log_info("Successfully loaded Smack network host rules.");
+                break;
+        default:
+                log_warning_errno(r, "Failed to load Smack network host rules: %m, ignoring.");
+                break;
         }
 
         *loaded_policy = true;
index d6b0c963e87526e35824e3035c6a7006236f6ffd..2e4173aabc08c16d0fe25b42d0b65a89e1ca3c34 100644 (file)
@@ -28,9 +28,9 @@
 #include <sys/epoll.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <linux/sctp.h>
 
 #include "sd-event.h"
-
 #include "alloc-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
@@ -877,8 +877,14 @@ static void socket_apply_socket_options(Socket *s, int fd) {
 
         if (s->no_delay) {
                 int b = s->no_delay;
-                if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
-                        log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+
+                if (s->socket_protocol == IPPROTO_SCTP) {
+                        if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &b, sizeof(b)) < 0)
+                                log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
+                } else {
+                        if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
+                                log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+                }
         }
 
         if (s->broadcast) {
index 2f163190e9d5535894d4bee1fe7a2bb03acb7281..bc85cef266bbb9ddc9091697adb0d7d9b685335a 100644 (file)
@@ -949,9 +949,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);
                                 }
index f935b6a601ac27abbabb4d7b1e6da44d49d2f19e..32267d95f53159826f3c13e966759f92d80c1eff 100644 (file)
@@ -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 :
@@ -3119,7 +3120,7 @@ int unit_kill_common(
                         killed = true;
         }
 
-        if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL))
+        if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL))
                 return -ESRCH;
 
         return r;
@@ -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);
index 469ee7af681d30490ca4b6515b4708f91aa3e8f7..cc5e9741fe990df18287fe616408ded3cfc9cc89 100644 (file)
@@ -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);
 }
index 03df7365b56d2683a0cf8cef5a23b10b11929c7d..bddc0c441a4b461dd05ab7578cb76bdbaa3945ae 100644 (file)
@@ -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;
 }
index 82f519958cc83a9cacd56d7735ef21b94f537d63..b44dbb14ea43bc23c0c38d18a40981f1ba4d3dd1 100644 (file)
@@ -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;
 }
index a8551ca9e8630a33499398136175ab94b00ff173..8a48bd7bf94deb7efcb08b70d47397f3e8db7192 100644 (file)
@@ -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");
 
index 006791a542186e0e8af7cc1b53a6c85df40e5aca..f5fe165fa33abb865e65284372b55d76f83ce909 100644 (file)
@@ -45,6 +45,8 @@
 #include "sigbus.h"
 #include "util.h"
 
+#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC)
+
 static char *arg_key_pem = NULL;
 static char *arg_cert_pem = NULL;
 static char *arg_trust_pem = NULL;
@@ -181,11 +183,13 @@ static ssize_t request_reader_entries(
                 } else if (r == 0) {
 
                         if (m->follow) {
-                                r = sd_journal_wait(m->journal, (uint64_t) -1);
+                                r = sd_journal_wait(m->journal, (uint64_t) JOURNAL_WAIT_TIMEOUT);
                                 if (r < 0) {
                                         log_error_errno(r, "Couldn't wait for journal event: %m");
                                         return MHD_CONTENT_READER_END_WITH_ERROR;
                                 }
+                                if (r == SD_JOURNAL_NOP)
+                                        break;
 
                                 continue;
                         }
@@ -241,6 +245,8 @@ static ssize_t request_reader_entries(
         }
 
         n = m->size - pos;
+        if (n < 1)
+                return 0;
         if (n > max)
                 n = max;
 
@@ -694,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);
 
@@ -834,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");
 
 
index b2f5fbf6b4caddaad2e0182893807ff563bf4594..2126606661fd515967f8dc051f5d9c614a8aa531 100644 (file)
@@ -587,7 +587,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"))
@@ -1181,6 +1181,7 @@ static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
 
 static int parse_config(void) {
         const ConfigTableItem items[] = {
+                { "Remote",  "Seal",                   config_parse_bool,             0, &arg_seal       },
                 { "Remote",  "SplitMode",              config_parse_write_split_mode, 0, &arg_split_mode },
                 { "Remote",  "ServerKeyFile",          config_parse_path,             0, &arg_key        },
                 { "Remote",  "ServerCertificateFile",  config_parse_path,             0, &arg_cert       },
index 3e32f34def6f661768005273b78033304234e0a2..7122d633625d81f7d7bf70eb14bbfc47eb5e107c 100644 (file)
@@ -1,4 +1,5 @@
 [Remote]
+# Seal=false
 # SplitMode=host
 # ServerKeyFile=@CERTIFICATEROOT@/private/journal-remote.pem
 # ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-remote.pem
index 3e8c4fa6d15533affb61610fb969a9d7ea8564a2..cba57403a3f13180d711e93f965150e0449bcfdd 100644 (file)
 
 #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. */
index f750ddfcbd625eb54a94d81bbfb39e8c1637db21..869c8fea03e472cfc28ee3e6fbf24acc3e33eed1 100644 (file)
@@ -527,7 +527,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;
index d009b2e93b5b6a905284663ae4ba63cce18c4c85..db11421e7a8354ad7726812529ae0ea3dab9be36 100644 (file)
@@ -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;
 
index 371df5b37f7bedd0f3e8baae25105cb8fc633888..f80a6ebfe52d28360985da5ac89400885f0fd5d1 100644 (file)
@@ -495,5 +495,9 @@ int server_open_native_socket(Server*s) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add native server fd to event loop: %m");
 
+        r = sd_event_source_set_priority(s->native_event_source, SD_EVENT_PRIORITY_NORMAL+5);
+        if (r < 0)
+                return log_error_errno(r, "Failed to adjust native event source priority: %m");
+
         return 0;
 }
index a8a9b7208025b05a6cb72d3eb9f9a85aa1becf5b..cfcc2c430209b767e66aa8d6704ef30b3c439f41 100644 (file)
@@ -1330,7 +1330,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)
index 131fcdac423aee7f7fd16bac469cfe88bd654b74..90884b69299266bb4855816e7ba95a1a72e0fb6f 100644 (file)
@@ -733,7 +733,7 @@ int server_open_stdout_socket(Server *s) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add stdout server fd to event source: %m");
 
-        r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10);
+        r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+5);
         if (r < 0)
                 return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m");
 
index cfc50d889ba91a53ecf887afb904c3ecab28cd80..0be73088e29bc09ef80476516bbdbc50bed2d1ad 100644 (file)
@@ -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);
         }
 
@@ -430,6 +430,10 @@ int server_open_syslog_socket(Server *s) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
 
+        r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
+        if (r < 0)
+                return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
+
         return 0;
 }
 
index e875ba4986d00b426d705850c5d04933e039a5c7..6fb80dda7a396ce292b87f2c28771466352bfc9d 100644 (file)
@@ -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"
 
@@ -839,7 +840,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;
index d3ea74404b82338e495ed3a326d60182d83de86e..1c696f9ef07a3288ef930f03799a40c55588a1ca 100644 (file)
@@ -145,12 +145,9 @@ static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
 
 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
 int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
+        bool system_description = false, system_name = false, chassis_id = false;
+        bool malformed = false, port_id = false, ttl = false, end = false;
         uint16_t type, len, i, l, t;
-        bool chassis_id = false;
-        bool malformed = false;
-        bool port_id = false;
-        bool ttl = false;
-        bool end = false;
         lldp_port *port;
         uint8_t *p, *q;
         sd_lldp *lldp;
@@ -163,8 +160,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
         lldp = (sd_lldp *) port->userdata;
 
         if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
-                log_lldp("Port is disabled : %s . Dropping ...",
-                         lldp->port->ifname);
+                log_lldp("Port: %s is disabled. Dropping.", lldp->port->ifname);
                 goto out;
         }
 
@@ -182,8 +178,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
 
                 if (type == LLDP_TYPE_END) {
                         if (len != 0) {
-                                log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
-                                         len);
+                                log_lldp("TLV type end must be length 0 (not %d). Dropping.", len);
 
                                 malformed = true;
                                 goto out;
@@ -193,8 +188,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
 
                         break;
                 } else if (type >=_LLDP_TYPE_MAX) {
-                        log_lldp("TLV type not recognized %d . Dropping ...",
-                                 type);
+                        log_lldp("TLV type: %d not recognized. Dropping.", type);
 
                         malformed = true;
                         goto out;
@@ -209,7 +203,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
 
                 if (i <= 3) {
                         if (i != type) {
-                                log_lldp("TLV missing or out of order. Dropping ...");
+                                log_lldp("TLV missing or out of order. Dropping.");
 
                                 malformed = true;
                                 goto out;
@@ -220,25 +214,22 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
                 case LLDP_TYPE_CHASSIS_ID:
 
                         if (len < 2) {
-                                log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
-                                         len);
+                                log_lldp("Received malformed Chassis ID TLV length: %d. Dropping.", len);
 
                                 malformed = true;
                                 goto out;
                         }
 
                         if (chassis_id) {
-                                log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
+                                log_lldp("Duplicate Chassis ID TLV found. Dropping.");
 
                                 malformed = true;
                                 goto out;
                         }
 
                         /* Look what subtype it has */
-                        if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
-                            *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
-                                log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
-                                         *q);
+                        if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED || *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
+                                log_lldp("Unknown subtype: %d found in Chassis ID TLV. Dropping.", *q);
 
                                 malformed = true;
                                 goto out;
@@ -251,25 +242,22 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
                 case LLDP_TYPE_PORT_ID:
 
                         if (len < 2) {
-                                log_lldp("Received malformed Port ID TLV len = %d. Dropping",
-                                         len);
+                                log_lldp("Received malformed Port ID TLV length: %d. Dropping.", len);
 
                                 malformed = true;
                                 goto out;
                         }
 
                         if (port_id) {
-                                log_lldp("Duplicate Port ID TLV found. Dropping ...");
+                                log_lldp("Duplicate Port ID TLV found. Dropping.");
 
                                 malformed = true;
                                 goto out;
                         }
 
                         /* Look what subtype it has */
-                        if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
-                            *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
-                                log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
-                                         *q);
+                        if (*q == LLDP_PORT_SUBTYPE_RESERVED || *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
+                                log_lldp("Unknown subtype: %d found in Port ID TLV. Dropping.", *q);
 
                                 malformed = true;
                                 goto out;
@@ -282,16 +270,14 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
                 case LLDP_TYPE_TTL:
 
                         if(len != 2) {
-                                log_lldp(
-                                         "Received invalid lenth: %d TTL TLV. Dropping ...",
-                                         len);
+                                log_lldp("Received invalid TTL TLV lenth: %d. Dropping.", len);
 
                                 malformed = true;
                                 goto out;
                         }
 
                         if (ttl) {
-                                log_lldp("Duplicate TTL TLV found. Dropping ...");
+                                log_lldp("Duplicate TTL TLV found. Dropping.");
 
                                 malformed = true;
                                 goto out;
@@ -299,12 +285,46 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
 
                         ttl = true;
 
+                        break;
+                case LLDP_TYPE_SYSTEM_NAME:
+
+                        /* According to RFC 1035 the length of a FQDN is limited to 255 characters */
+                        if (len > 255) {
+                                log_lldp("Received invalid system name length: %d. Dropping.", len);
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (system_name) {
+                                log_lldp("Duplicate system name found. Dropping.");
+                                malformed = true;
+                                goto out;
+                        }
+
+                        system_name = true;
+
+                        break;
+                case LLDP_TYPE_SYSTEM_DESCRIPTION:
+
+                        /* 0 <= n <= 255 octets */
+                        if (len > 255) {
+                                log_lldp("Received invalid system description length: %d. Dropping.", len);
+                                malformed = true;
+                                goto out;
+                        }
+
+                        if (system_description) {
+                                log_lldp("Duplicate system description found. Dropping.");
+                                malformed = true;
+                                goto out;
+                        }
+
+                        system_description = true;
                         break;
                 default:
 
                         if (len == 0) {
-                                log_lldp("TLV type = %d's, length 0 received . Dropping ...",
-                                         type);
+                                log_lldp("TLV type: %d length 0 received. Dropping.", type);
 
                                 malformed = true;
                                 goto out;
@@ -314,7 +334,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
         }
 
         if(!chassis_id || !port_id || !ttl || !end) {
-                log_lldp( "One or more mandotory TLV missing . Dropping ...");
+                log_lldp("One or more mandatory TLV missing. Dropping.");
 
                 malformed = true;
                 goto out;
@@ -323,7 +343,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
 
         r = tlv_packet_parse_pdu(tlv, length);
         if (r < 0) {
-                log_lldp( "Failed to parse the TLV. Dropping ...");
+                log_lldp("Failed to parse the TLV. Dropping.");
 
                 malformed = true;
                 goto out;
index d8154f05877d13165aeeceef59b188a93b2f6446..0ee466b32adc345a9a57d5ee5c535710a695420c 100644 (file)
@@ -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;
index 8d486fcbbdd62a3297df74f9490bbe8ba4366c9e..9ddc9b5aaf261ef5ecaf95c14c6335b4e47a8a08 100644 (file)
@@ -67,12 +67,19 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
 
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS,              ESRCH),
 
-        SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS,              EIO),
+        SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS,              ESRCH),
         SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY,                EINVAL),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR,                   ENOENT),
         SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES,                 ENOMEM),
         SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP,                   EDEADLK),
         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_NO_SUCH_TRANSFER,             ENXIO),
         SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS,         EBUSY),
index f2092795f410b40c3142338bbc8d2495980040b2..e93b6ac4483f02b596f6870b13020f918002e963 100644 (file)
 #define BUS_ERROR_NO_RESOURCES "org.freedesktop.resolve1.NoResources"
 #define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop"
 #define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted"
+#define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure"
+#define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService"
+#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_DNS "org.freedesktop.resolve1.DnsError."
 
 #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
index 404eaa3c895bbb65aa8d3071e3276b02219d0223..c77eb5fd0381517f94d566cc663a5d50df9c4848 100644 (file)
@@ -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;
 
index 6c05444e9a057b172c4395a9fbbbc7f76b562630..e7d6170eecf55af6ea4354037e402f1ee6e0db59 100644 (file)
@@ -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;
         }
 
index 1c3ccda3648724c86df623b8fdfabda65782bbd6..cbc450fdb22ea36530143557a5064f323f75e1d1 100644 (file)
@@ -36,7 +36,7 @@ static void test_bus_new(void) {
 }
 
 static int test_bus_open(void) {
-        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
         r = sd_bus_open_system(&bus);
@@ -59,8 +59,8 @@ static void test_bus_new_method_call(void) {
 
         printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref));
 
-        sd_bus_unref(bus);
-        printf("after bus_unref: refcount %u\n", m->n_ref);
+        sd_bus_flush_close_unref(bus);
+        printf("after bus_flush_close_unref: refcount %u\n", m->n_ref);
 }
 
 static void test_bus_new_signal(void) {
@@ -73,8 +73,8 @@ static void test_bus_new_signal(void) {
 
         printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref));
 
-        sd_bus_unref(bus);
-        printf("after bus_unref: refcount %u\n", m->n_ref);
+        sd_bus_flush_close_unref(bus);
+        printf("after bus_flush_close_unref: refcount %u\n", m->n_ref);
 }
 
 int main(int argc, char **argv) {
index c52405463ed353ecc4342f46124981239e219394..407fd14555ccae205b3ed81f87af726fca95d53f 100644 (file)
@@ -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[]) {
index 077cc6ddac359af1f743cc3dfef4bdb50445ad07..b9d1ea5217febb7d9bf6a2c1cfd61ecd8c2d66e9 100644 (file)
@@ -246,6 +246,8 @@ int main(int argc, char *argv[]) {
                         log_error("%s", error.message);
                 else
                         dbus_message_unref(w);
+
+                dbus_error_free(&error);
         }
 #endif
 
index f44054a7b529af7c1b00f265232d6b012f43140e..9633e46ce08b5c9f196c7e3019c49b3246170acd 100644 (file)
@@ -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;
index 7fccbd1a71052f6cfeb65e7cb47f1733e4606d24..11c7330b9bef9684404438a6e4fd92f26f83f9a0 100644 (file)
@@ -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;
         }
index 9417a8d1d15c0890e862560baeec57871bc30097..c605b18ae97294dec24e75aa6d2f78544f97c592 100644 (file)
@@ -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;
index 4b46eeb533a5b158d6b1c8ccf45a2243960e7145..ef240c35310a03be577902beeca6a0745f40d95d 100644 (file)
@@ -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)
index 218120101707e5f0052490e47d82a82000bb2d52..e95c99af0d3e1d5fb874863860d7ba6eee36f173 100644 (file)
@@ -52,7 +52,7 @@ static int broadcast_groups_get(sd_netlink *nl) {
         int r;
 
         assert(nl);
-        assert(nl->fd > 0);
+        assert(nl->fd >= 0);
 
         r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
         if (r < 0) {
index 135354e5f3478ecac03469fbcd44cab2572be9da..be4ab1373d75296a4380b868560096be6ea60b89 100644 (file)
@@ -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 },
index efbceba83d114c18aa3ba7dae0d7f2b988858576..c1f5867ee4aec859040e2cd539721c122ea2f79d 100644 (file)
@@ -99,17 +99,17 @@ _public_ int sd_network_get_domains(char ***ret) {
         return network_get_strv("DOMAINS", ret);
 }
 
-_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
+static int network_link_get_string(int ifindex, const char *field, char **ret) {
         _cleanup_free_ char *s = NULL, *p = NULL;
         int r;
 
         assert_return(ifindex > 0, -EINVAL);
-        assert_return(state, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
+        if (asprintf(&p, "/run/systemd/netif/links/%i", ifindex) < 0)
                 return -ENOMEM;
 
-        r = parse_env_file(p, NEWLINE, "ADMIN_STATE", &s, NULL);
+        r = parse_env_file(p, NEWLINE, field, &s, NULL);
         if (r == -ENOENT)
                 return -ENODATA;
         if (r < 0)
@@ -117,82 +117,72 @@ _public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
         if (isempty(s))
                 return -ENODATA;
 
-        *state = s;
+        *ret = s;
         s = NULL;
 
         return 0;
 }
 
-_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
-        _cleanup_free_ char *s = NULL, *p = NULL;
+static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
+        _cleanup_free_ char *p = NULL, *s = NULL;
+        _cleanup_strv_free_ char **a = NULL;
         int r;
 
         assert_return(ifindex > 0, -EINVAL);
-        assert_return(filename, -EINVAL);
+        assert_return(ret, -EINVAL);
 
         if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
                 return -ENOMEM;
 
-        r = parse_env_file(p, NEWLINE, "NETWORK_FILE", &s, NULL);
+        r = parse_env_file(p, NEWLINE, key, &s, NULL);
         if (r == -ENOENT)
                 return -ENODATA;
         if (r < 0)
                 return r;
-        if (isempty(s))
-                return -ENODATA;
-
-        *filename = s;
-        s = NULL;
+        if (isempty(s)) {
+                *ret = NULL;
+                return 0;
+        }
 
-        return 0;
-}
+        a = strv_split(s, " ");
+        if (!a)
+                return -ENOMEM;
 
-_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
-        _cleanup_free_ char *s = NULL, *p = NULL;
-        int r;
+        strv_uniq(a);
+        r = strv_length(a);
 
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(state, -EINVAL);
+        *ret = a;
+        a = NULL;
 
-        if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
-                return -ENOMEM;
+        return r;
+}
 
-        r = parse_env_file(p, NEWLINE, "OPER_STATE", &s, NULL);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
-        if (isempty(s))
-                return -ENODATA;
+_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
+        return network_link_get_string(ifindex, "ADMIN_STATE", state);
+}
 
-        *state = s;
-        s = NULL;
+_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
+        return network_link_get_string(ifindex, "NETWORK_FILE", filename);
+}
 
-        return 0;
+_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
+        return network_link_get_string(ifindex, "OPER_STATE", state);
 }
 
 _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
-        _cleanup_free_ char *s = NULL, *p = NULL;
-        int r;
-
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(llmnr, -EINVAL);
-
-        if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
-                return -ENOMEM;
+        return network_link_get_string(ifindex, "LLMNR", llmnr);
+}
 
-        r = parse_env_file(p, NEWLINE, "LLMNR", &s, NULL);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
-        if (isempty(s))
-                return -ENODATA;
+_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) {
+        return network_link_get_string(ifindex, "MDNS", mdns);
+}
 
-        *llmnr = s;
-        s = NULL;
+_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) {
+        return network_link_get_string(ifindex, "DNSSEC", dnssec);
+}
 
-        return 0;
+_public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) {
+        return network_link_get_strv(ifindex, "DNSSEC_NTA", nta);
 }
 
 _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
@@ -221,85 +211,32 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
 }
 
 int sd_network_link_get_timezone(int ifindex, char **ret) {
-        _cleanup_free_ char *s = NULL, *p = NULL;
-        int r;
-
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
-                return -ENOMEM;
-
-        r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
-        if (isempty(s))
-                return -ENODATA;
-
-        *ret = s;
-        s = NULL;
-        return 0;
-}
-
-static int network_get_link_strv(const char *key, int ifindex, char ***ret) {
-        _cleanup_free_ char *p = NULL, *s = NULL;
-        _cleanup_strv_free_ char **a = NULL;
-        int r;
-
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
-                return -ENOMEM;
-
-        r = parse_env_file(p, NEWLINE, key, &s, NULL);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
-        if (isempty(s)) {
-                *ret = NULL;
-                return 0;
-        }
-
-        a = strv_split(s, " ");
-        if (!a)
-                return -ENOMEM;
-
-        strv_uniq(a);
-        r = strv_length(a);
-
-        *ret = a;
-        a = NULL;
-
-        return r;
+        return network_link_get_string(ifindex, "TIMEZONE", ret);
 }
 
 _public_ int sd_network_link_get_dns(int ifindex, char ***ret) {
-        return network_get_link_strv("DNS", ifindex, ret);
+        return network_link_get_strv(ifindex, "DNS", ret);
 }
 
 _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
-        return network_get_link_strv("NTP", ifindex, ret);
+        return network_link_get_strv(ifindex, "NTP", ret);
 }
 
 _public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
-        return network_get_link_strv("DOMAINS", ifindex, ret);
+        return network_link_get_strv(ifindex, "DOMAINS", ret);
 }
 
 _public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
-        return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret);
+        return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret);
 }
 
 _public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
-        return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret);
+        return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret);
 }
 
 _public_ int sd_network_link_get_wildcard_domain(int ifindex) {
-        int r;
         _cleanup_free_ char *p = NULL, *s = NULL;
+        int r;
 
         assert_return(ifindex > 0, -EINVAL);
 
index e78a75c9eaa87a3c067674d06c79d215e96cd928..ce97e81ed6c0c4a11c98068ce98f7ab100d0e426 100644 (file)
@@ -101,11 +101,11 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 log_error_errno(r, "sd_resolve_getnameinfo(): %m");
 
-        /* Wait until the two queries are completed */
-        while (sd_resolve_query_is_done(q1) == 0 ||
-               sd_resolve_query_is_done(q2) == 0) {
-
+        /* Wait until all queries are completed */
+        for (;;) {
                 r = sd_resolve_wait(resolve, (uint64_t) -1);
+                if (r == 0)
+                        break;
                 if (r < 0) {
                         log_error_errno(r, "sd_resolve_wait(): %m");
                         assert_not_reached("sd_resolve_wait() failed");
index 5ca41331bdb487c868b9c9b4758d452a772bf6b0..8ab845eb803cd41d16bb26abb9a99809286acaf9 100644 (file)
@@ -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;
                 }
index 816349c559e758986f39c2cb81ac9950135fa958..931b96fe514c76e249430aca7c0c4156a8484d4c 100644 (file)
@@ -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) {
index d51330fb85a227c1dbaa36a05c95a353a89ec953..2e14aa2d951846e33326c7ef0a1195a07b1b82e6 100644 (file)
@@ -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);
 }
index 4631f5fc90301dcbe04b7be89fcd8fb21de51107..9eda4638e5bae54ed7de9f1cc840086636e215aa 100644 (file)
@@ -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) {
index 1f4936cebe4f8a77bf11aa53c4c41e83e5905338..9d111f737c9272529eeddca4b7d0af5430b59c0e 100644 (file)
@@ -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;
index 4ad9740e5e9314c7db90b27dfbc4f4e8dd41f0b0..98f8ea3c786629df6be4e6a21576a6badd02a4a4 100644 (file)
@@ -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;
                 }
index d805bcfdca7d837d9515d5b400400377da5f4be6..9d19307236bf03fa9e2d3d52b5b573a67ce40a88 100644 (file)
@@ -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);
index 9f7c9952d3ec99e0431a06dae7d7c1fdd657c3a9..03d32c6ed74a6b05366edf57f8394db97ee4983a 100644 (file)
@@ -922,7 +922,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
 
            First, we start by creating a private playground in /tmp,
            that we can mount MS_SLAVE. (Which is necessary, since
-           MS_MOUNT cannot be applied to mounts with MS_SHARED parent
+           MS_MOVE cannot be applied to mounts with MS_SHARED parent
            mounts.) */
 
         if (!mkdtemp(mount_slave))
index 685bbafdf1f326e4c7183f96ade1ed60c0e14abd..fd454310ae23d35d9e4220a0c250c9050f366fa4 100644 (file)
@@ -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;
 
index c6b5b1ec44d68863390eac4dec960a1c2fd3b77f..e448dd2035fb108fc87a4731e74a3ae98fd1f0bb 100644 (file)
@@ -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;
index 0234825adb7bae57bde9140d5fb342e018647f98..4a8fa4d8f3c64809bd9080fb5f811fe6497cf44f 100644 (file)
@@ -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)
index d09a3c2d07d1e38a85f6f6acde59143dfcb0ecfd..4d6ac747fd10b06de08f905f307c8cdfc5202f52 100644 (file)
@@ -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);
index 9811526c6d37e21b586aae78868925e7e3d0a693..10fec5e75f3347fde0487a60109059a056f3a9fb 100644 (file)
@@ -2495,7 +2495,7 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address) {
         link->ipv6ll_address = *address;
         link_check_ready(link);
 
-        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
                 r = link_acquire_ipv6_conf(link);
                 if (r < 0) {
                         link_enter_failed(link);
@@ -2511,7 +2511,7 @@ static int link_carrier_gained(Link *link) {
 
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
                 r = link_acquire_conf(link);
                 if (r < 0) {
                         link_enter_failed(link);
@@ -2868,6 +2868,26 @@ int link_save(Link *link) {
 
                 fprintf(f, "LLMNR=%s\n",
                         resolve_support_to_string(link->network->llmnr));
+                fprintf(f, "MDNS=%s\n",
+                        resolve_support_to_string(link->network->mdns));
+
+                if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
+                        fprintf(f, "DNSSEC=%s\n",
+                                dnssec_mode_to_string(link->network->dnssec_mode));
+
+                if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
+                        const char *n;
+
+                        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;
+                        }
+                        fputc('\n', f);
+                }
 
                 fputs("ADDRESSES=", f);
                 space = false;
@@ -2881,7 +2901,6 @@ int link_save(Link *link) {
                         fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
                         space = true;
                 }
-
                 fputc('\n', f);
 
                 fputs("ROUTES=", f);
index de2c66d15372493d8a8f215844ef09b4618adb26..2f2a36ccca802e2d102f3492d5185523c674e21a 100644 (file)
@@ -45,7 +45,10 @@ Network.Address,                        config_parse_address,
 Network.Gateway,                        config_parse_gateway,                           0,                             0
 Network.Domains,                        config_parse_domains,                           0,                             offsetof(Network, domains)
 Network.DNS,                            config_parse_strv,                              0,                             offsetof(Network, dns)
-Network.LLMNR,                          config_parse_resolve,                           0,                             offsetof(Network, llmnr)
+Network.LLMNR,                          config_parse_resolve_support,                   0,                             offsetof(Network, llmnr)
+Network.MulticastDNS,                   config_parse_resolve_support,                   0,                             offsetof(Network, mdns)
+Network.DNSSEC,                         config_parse_dnssec_mode,                       0,                             offsetof(Network, dnssec_mode)
+Network.DNSSECNegativeTrustAnchors,     config_parse_dnssec_negative_trust_anchors,     0,                             offsetof(Network, dnssec_negative_trust_anchors)
 Network.NTP,                            config_parse_strv,                              0,                             offsetof(Network, ntp)
 Network.IPForward,                      config_parse_address_family_boolean_with_kernel,0,                             offsetof(Network, ip_forward)
 Network.IPMasquerade,                   config_parse_bool,                              0,                             offsetof(Network, ip_masquerade)
index 29723a852fb6d32a951a14803518f81a2cf29a16..c11cb3dcb3c48fa33ff5d64f50dc8a889b4b4e2c 100644 (file)
@@ -32,6 +32,7 @@
 #include "networkd-network.h"
 #include "networkd.h"
 #include "parse-util.h"
+#include "set.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -121,6 +122,8 @@ static int network_load_one(Manager *manager, const char *filename) {
         network->unicast_flood = true;
 
         network->llmnr = RESOLVE_SUPPORT_YES;
+        network->mdns = RESOLVE_SUPPORT_NO;
+        network->dnssec_mode = _DNSSEC_MODE_INVALID;
 
         network->link_local = ADDRESS_FAMILY_IPV6;
 
@@ -275,6 +278,8 @@ void network_free(Network *network) {
         free(network->dhcp_server_dns);
         free(network->dhcp_server_ntp);
 
+        set_free_free(network->dnssec_negative_trust_anchors);
+
         free(network);
 }
 
@@ -908,3 +913,55 @@ int config_parse_dhcp_server_ntp(
                 n->dhcp_server_ntp = m;
         }
 }
+
+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) {
+
+        const char *p = rvalue;
+        Network *n = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                r = dns_name_is_valid(w);
+                if (r <= 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
+                        continue;
+                }
+
+                r = set_put(n->dnssec_negative_trust_anchors, w);
+                if (r < 0)
+                        return log_oom();
+                if (r > 0)
+                        w = NULL;
+        }
+
+        return 0;
+}
index cb3a50d9badb59cbb48e448a41386f02736c0d8c..b07fa41abcfc4d74dc012f9f73cb95fd39bbe873 100644 (file)
@@ -22,6 +22,7 @@
 ***/
 
 #include "condition.h"
+#include "resolve-util.h"
 
 typedef struct Network Network;
 
@@ -144,6 +145,9 @@ struct Network {
         char **domains, **dns, **ntp, **bind_carrier;
 
         ResolveSupport llmnr;
+        ResolveSupport mdns;
+        DnssecMode dnssec_mode;
+        Set *dnssec_negative_trust_anchors;
 
         LIST_FIELDS(Network, networks);
 };
@@ -170,6 +174,7 @@ int config_parse_hostname(const char *unit, const char *filename, unsigned line,
 int config_parse_timezone(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_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);
 
 /* 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);
index 2545621a93d448c17a955eb2d24e8fe107c6b039..93135bb658644a9bfc06c4d34613807c44b7df67 100644 (file)
@@ -101,54 +101,3 @@ int config_parse_address_family_boolean_with_kernel(
 
         return 0;
 }
-
-static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
-        [RESOLVE_SUPPORT_NO] = "no",
-        [RESOLVE_SUPPORT_YES] = "yes",
-        [RESOLVE_SUPPORT_RESOLVE] = "resolve",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport);
-
-int config_parse_resolve(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        ResolveSupport *resolve = data;
-        int k;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(resolve);
-
-        /* Our enum shall be a superset of booleans, hence first try
-         * to parse as boolean, and then as enum */
-
-        k = parse_boolean(rvalue);
-        if (k > 0)
-                *resolve = RESOLVE_SUPPORT_YES;
-        else if (k == 0)
-                *resolve = RESOLVE_SUPPORT_NO;
-        else {
-                ResolveSupport s;
-
-                s = resolve_support_from_string(rvalue);
-                if (s < 0){
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue);
-                        return 0;
-                }
-
-                *resolve = s;
-        }
-
-        return 0;
-}
index cc41aae85ae038006b3df12a4c1dfcb5f7a16b62..021ce4b128c0ec9d5c5d05823f978579e04df6a7 100644 (file)
@@ -33,20 +33,8 @@ typedef enum AddressFamilyBoolean {
         _ADDRESS_FAMILY_BOOLEAN_INVALID = -1,
 } AddressFamilyBoolean;
 
-typedef enum ResolveSupport {
-        RESOLVE_SUPPORT_NO,
-        RESOLVE_SUPPORT_YES,
-        RESOLVE_SUPPORT_RESOLVE,
-        _RESOLVE_SUPPORT_MAX,
-        _RESOLVE_SUPPORT_INVALID = -1,
-} ResolveSupport;
-
-int config_parse_resolve(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_address_family_boolean(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_address_family_boolean_with_kernel(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* resolve_support_to_string(ResolveSupport i) _const_;
-ResolveSupport resolve_support_from_string(const char *s) _pure_;
-
 const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_;
 AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_;
index 270bcf010fcfa46be24af468a1791818188b3af8..3c0e26ea5a33cb1ff73c37f3a9c716653c7750f1 100644 (file)
@@ -54,6 +54,7 @@ int chown_cgroup(pid_t pid, uid_t uid_shift) {
                        "tasks",
                        "notify_on_release",
                        "cgroup.procs",
+                       "cgroup.events",
                        "cgroup.clone_children",
                        "cgroup.controllers",
                        "cgroup.subtree_control",
index aa6a16309c988d416c7610c9678dc09a114ac7af..014a40b24314915d4cf090ade26dd2b88c9ee871 100644 (file)
@@ -261,10 +261,10 @@ int change_uid_gid(const char *user, char **_home) {
                 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
 
         if (setresgid(gid, gid, gid) < 0)
-                return log_error_errno(errno, "setregid() failed: %m");
+                return log_error_errno(errno, "setresgid() failed: %m");
 
         if (setresuid(uid, uid, uid) < 0)
-                return log_error_errno(errno, "setreuid() failed: %m");
+                return log_error_errno(errno, "setresuid() failed: %m");
 
         if (_home) {
                 *_home = home;
index a4e13bd6aa1ee28691e4bdc1ce2802bc9438ebc8..d619206dd6ef2d5088c5313125a8a1afb192f642 100644 (file)
@@ -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) {
index 3e4b52a3a9974cd8cca7d0c1a155720971ac06b5..54a060ea5a94cd0522ef3ad9a16efc25aad9ea5b 100644 (file)
@@ -33,6 +33,7 @@
 #include "parse-util.h"
 #include "resolved-def.h"
 #include "resolved-dns-packet.h"
+#include "terminal-util.h"
 
 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
 
@@ -42,7 +43,14 @@ static uint16_t arg_type = 0;
 static uint16_t arg_class = 0;
 static bool arg_legend = true;
 static uint64_t arg_flags = 0;
-static bool arg_resolve_service = false;
+
+static enum {
+        MODE_RESOLVE_HOST,
+        MODE_RESOLVE_RECORD,
+        MODE_RESOLVE_SERVICE,
+        MODE_STATISTICS,
+        MODE_RESET_STATISTICS,
+} arg_mode = MODE_RESOLVE_HOST;
 
 static void print_source(uint64_t flags, usec_t rtt) {
         char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -56,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));
 
@@ -320,8 +330,7 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres
         return 0;
 }
 
-static int resolve_record(sd_bus *bus, const char *name) {
-
+static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) {
         _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;
         char ifname[IF_NAMESIZE] = "";
@@ -335,7 +344,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
         if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
                 return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
 
-        log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(arg_class), dns_type_to_string(arg_type), isempty(ifname) ? "*" : ifname);
+        log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname);
 
         r = sd_bus_message_new_method_call(
                         bus,
@@ -347,7 +356,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
+        r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, arg_flags);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -368,7 +377,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
         while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
                 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-                _cleanup_free_ char *s = NULL;
+                const char *s;
                 uint16_t c, t;
                 int ifindex;
                 const void *d;
@@ -399,16 +408,12 @@ static int resolve_record(sd_bus *bus, const char *name) {
                         return log_oom();
 
                 r = dns_packet_read_rr(p, &rr, NULL, NULL);
-                if (r < 0) {
-                        log_error("Failed to parse RR.");
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse RR: %m");
 
-                r = dns_resource_record_to_string(rr, &s);
-                if (r < 0) {
-                        log_error("Failed to format RR.");
-                        return r;
-                }
+                s = dns_resource_record_to_string(rr);
+                if (!s)
+                        return log_oom();
 
                 ifname[0] = 0;
                 if (ifindex > 0 && !if_indextoname(ifindex, ifname))
@@ -438,6 +443,127 @@ static int resolve_record(sd_bus *bus, const char *name) {
         return 0;
 }
 
+static int resolve_rfc4501(sd_bus *bus, const char *name) {
+        uint16_t type = 0, class = 0;
+        const char *p, *q, *n;
+        int r;
+
+        assert(bus);
+        assert(name);
+        assert(startswith(name, "dns:"));
+
+        /* Parse RFC 4501 dns: URIs */
+
+        p = name + 4;
+
+        if (p[0] == '/') {
+                const char *e;
+
+                if (p[1] != '/')
+                        goto invalid;
+
+                e = strchr(p + 2, '/');
+                if (!e)
+                        goto invalid;
+
+                if (e != p + 2)
+                        log_warning("DNS authority specification not supported; ignoring specified authority.");
+
+                p = e + 1;
+        }
+
+        q = strchr(p, '?');
+        if (q) {
+                n = strndupa(p, q - p);
+                q++;
+
+                for (;;) {
+                        const char *f;
+
+                        f = startswith_no_case(q, "class=");
+                        if (f) {
+                                _cleanup_free_ char *t = NULL;
+                                const char *e;
+
+                                if (class != 0) {
+                                        log_error("DNS class specified twice.");
+                                        return -EINVAL;
+                                }
+
+                                e = strchrnul(f, ';');
+                                t = strndup(f, e - f);
+                                if (!t)
+                                        return log_oom();
+
+                                r = dns_class_from_string(t);
+                                if (r < 0) {
+                                        log_error("Unknown DNS class %s.", t);
+                                        return -EINVAL;
+                                }
+
+                                class = r;
+
+                                if (*e == ';') {
+                                        q = e + 1;
+                                        continue;
+                                }
+
+                                break;
+                        }
+
+                        f = startswith_no_case(q, "type=");
+                        if (f) {
+                                _cleanup_free_ char *t = NULL;
+                                const char *e;
+
+                                if (type != 0) {
+                                        log_error("DNS type specified twice.");
+                                        return -EINVAL;
+                                }
+
+                                e = strchrnul(f, ';');
+                                t = strndup(f, e - f);
+                                if (!t)
+                                        return log_oom();
+
+                                r = dns_type_from_string(t);
+                                if (r < 0) {
+                                        log_error("Unknown DNS type %s.", t);
+                                        return -EINVAL;
+                                }
+
+                                type = r;
+
+                                if (*e == ';') {
+                                        q = e + 1;
+                                        continue;
+                                }
+
+                                break;
+                        }
+
+                        goto invalid;
+                }
+        } else
+                n = p;
+
+        if (type == 0)
+                type = arg_type;
+        if (type == 0)
+                type = DNS_TYPE_A;
+
+        if (class == 0)
+                class = arg_class;
+        if (class == 0)
+                class = DNS_CLASS_IN;
+
+        return resolve_record(bus, n, class, type);
+
+invalid:
+        log_error("Invalid DNS URI: %s", name);
+        return -EINVAL;
+}
+
 static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
         const char *canonical_name, *canonical_type, *canonical_domain;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
@@ -639,6 +765,141 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
         return 0;
 }
 
+static int show_statistics(sd_bus *bus) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        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, 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",
+                                "org.freedesktop.resolve1.Manager",
+                                "TransactionStatistics",
+                                &error,
+                                &reply,
+                                "(tt)");
+        if (r < 0)
+                return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "(tt)",
+                                &n_current_transactions,
+                                &n_total_transactions);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        printf("%sTransactions%s\n"
+               "Current Transactions: %" PRIu64 "\n"
+               "  Total Transactions: %" PRIu64 "\n",
+               ansi_highlight(),
+               ansi_normal(),
+               n_current_transactions,
+               n_total_transactions);
+
+        reply = sd_bus_message_unref(reply);
+
+        r = sd_bus_get_property(bus,
+                                "org.freedesktop.resolve1",
+                                "/org/freedesktop/resolve1",
+                                "org.freedesktop.resolve1.Manager",
+                                "CacheStatistics",
+                                &error,
+                                &reply,
+                                "(ttt)");
+        if (r < 0)
+                return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "(ttt)",
+                                &cache_size,
+                                &n_cache_hit,
+                                &n_cache_miss);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        printf("\n%sCache%s\n"
+               "  Current Cache Size: %" PRIu64 "\n"
+               "          Cache Hits: %" PRIu64 "\n"
+               "        Cache Misses: %" PRIu64 "\n",
+               ansi_highlight(),
+               ansi_normal(),
+               cache_size,
+               n_cache_hit,
+               n_cache_miss);
+
+        reply = sd_bus_message_unref(reply);
+
+        r = sd_bus_get_property(bus,
+                                "org.freedesktop.resolve1",
+                                "/org/freedesktop/resolve1",
+                                "org.freedesktop.resolve1.Manager",
+                                "DNSSECStatistics",
+                                &error,
+                                &reply,
+                                "(tttt)");
+        if (r < 0)
+                return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "(tttt)",
+                                &n_dnssec_secure,
+                                &n_dnssec_insecure,
+                                &n_dnssec_bogus,
+                                &n_dnssec_indeterminate);
+        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",
+               ansi_highlight(),
+               ansi_normal(),
+               n_dnssec_secure,
+               n_dnssec_insecure,
+               n_dnssec_bogus,
+               n_dnssec_indeterminate);
+
+        return 0;
+}
+
+static int reset_statistics(sd_bus *bus) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        r = sd_bus_call_method(bus,
+                               "org.freedesktop.resolve1",
+                               "/org/freedesktop/resolve1",
+                               "org.freedesktop.resolve1.Manager",
+                               "ResetStatistics",
+                               &error,
+                               NULL,
+                               NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
 static void help_dns_types(void) {
         int i;
         const char *t;
@@ -673,7 +934,7 @@ static void help(void) {
                "     --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"
@@ -683,6 +944,8 @@ static void help(void) {
                "     --cname=BOOL           Do [not] follow CNAME redirects\n"
                "     --search=BOOL          Do [not] use search domains\n"
                "     --legend=BOOL          Do [not] print column headers\n"
+               "     --statistics           Show resolver statistics\n"
+               "     --reset-statistics     Reset resolver statistics\n"
                , program_invocation_short_name, program_invocation_short_name);
 }
 
@@ -695,20 +958,25 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SERVICE_ADDRESS,
                 ARG_SERVICE_TXT,
                 ARG_SEARCH,
+                ARG_STATISTICS,
+                ARG_RESET_STATISTICS,
         };
 
         static const struct option options[] = {
-                { "help",            no_argument,       NULL, 'h'                 },
-                { "version",         no_argument,       NULL, ARG_VERSION         },
-                { "type",            required_argument, NULL, 't'                 },
-                { "class",           required_argument, NULL, 'c'                 },
-                { "legend",          required_argument, NULL, ARG_LEGEND          },
-                { "protocol",        required_argument, NULL, 'p'                 },
-                { "cname",           required_argument, NULL, ARG_CNAME           },
-                { "service",         no_argument,       NULL, ARG_SERVICE         },
-                { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
-                { "service-txt",     required_argument, NULL, ARG_SERVICE_TXT     },
-                { "search",          required_argument, NULL, ARG_SEARCH          },
+                { "help",             no_argument,       NULL, 'h'                  },
+                { "version",          no_argument,       NULL, ARG_VERSION          },
+                { "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          },
+                { "service-address",  required_argument, NULL, ARG_SERVICE_ADDRESS  },
+                { "service-txt",      required_argument, NULL, ARG_SERVICE_TXT      },
+                { "search",           required_argument, NULL, ARG_SEARCH           },
+                { "statistics",       no_argument,       NULL, ARG_STATISTICS,      },
+                { "reset-statistics", no_argument,       NULL, ARG_RESET_STATISTICS },
                 {}
         };
 
@@ -765,6 +1033,7 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_type = (uint16_t) r;
                         assert((int) arg_type == r);
 
+                        arg_mode = MODE_RESOLVE_RECORD;
                         break;
 
                 case 'c':
@@ -808,7 +1077,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_SERVICE:
-                        arg_resolve_service = true;
+                        arg_mode = MODE_RESOLVE_SERVICE;
                         break;
 
                 case ARG_CNAME:
@@ -851,6 +1120,14 @@ static int parse_argv(int argc, char *argv[]) {
                                 arg_flags &= ~SD_RESOLVED_NO_SEARCH;
                         break;
 
+                case ARG_STATISTICS:
+                        arg_mode = MODE_STATISTICS;
+                        break;
+
+                case ARG_RESET_STATISTICS:
+                        arg_mode = MODE_RESET_STATISTICS;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -863,7 +1140,7 @@ static int parse_argv(int argc, char *argv[]) {
                 return -EINVAL;
         }
 
-        if (arg_type != 0 && arg_resolve_service) {
+        if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) {
                 log_error("--service and --type= may not be combined.");
                 return -EINVAL;
         }
@@ -871,6 +1148,9 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_type != 0 && arg_class == 0)
                 arg_class = DNS_CLASS_IN;
 
+        if (arg_class != 0 && arg_type == 0)
+                arg_type = DNS_TYPE_A;
+
         return 1 /* work to do */;
 }
 
@@ -885,20 +1165,61 @@ int main(int argc, char **argv) {
         if (r <= 0)
                 goto finish;
 
-        if (optind >= argc) {
-                log_error("No arguments passed");
-                r = -EINVAL;
-                goto finish;
-        }
-
         r = sd_bus_open_system(&bus);
         if (r < 0) {
                 log_error_errno(r, "sd_bus_open_system: %m");
                 goto finish;
         }
 
-        if (arg_resolve_service) {
+        switch (arg_mode) {
 
+        case MODE_RESOLVE_HOST:
+                if (optind >= argc) {
+                        log_error("No arguments passed");
+                        r = -EINVAL;
+                        goto finish;
+                }
+
+                while (argv[optind]) {
+                        int family, ifindex, k;
+                        union in_addr_union a;
+
+                        if (startswith(argv[optind], "dns:"))
+                                k = resolve_rfc4501(bus, argv[optind]);
+                        else {
+                                k = parse_address(argv[optind], &family, &a, &ifindex);
+                                if (k >= 0)
+                                        k = resolve_address(bus, family, &a, ifindex);
+                                else
+                                        k = resolve_host(bus, argv[optind]);
+                        }
+
+                        if (r == 0)
+                                r = k;
+
+                        optind++;
+                }
+                break;
+
+        case MODE_RESOLVE_RECORD:
+                if (optind >= argc) {
+                        log_error("No arguments passed");
+                        r = -EINVAL;
+                        goto finish;
+                }
+
+                while (argv[optind]) {
+                        int k;
+
+                        k = resolve_record(bus, argv[optind], arg_class, arg_type);
+                        if (r == 0)
+                                r = k;
+
+                        optind++;
+                }
+                break;
+
+        case MODE_RESOLVE_SERVICE:
                 if (argc < optind + 1) {
                         log_error("Domain specification required.");
                         r = -EINVAL;
@@ -916,27 +1237,27 @@ int main(int argc, char **argv) {
                         goto finish;
                 }
 
-                goto finish;
-        }
-
-        while (argv[optind]) {
-                int family, ifindex, k;
-                union in_addr_union a;
+                break;
 
-                if (arg_type != 0)
-                        k = resolve_record(bus, argv[optind]);
-                else {
-                        k = parse_address(argv[optind], &family, &a, &ifindex);
-                        if (k >= 0)
-                                k = resolve_address(bus, family, &a, ifindex);
-                        else
-                                k = resolve_host(bus, argv[optind]);
+        case MODE_STATISTICS:
+                if (argc > optind) {
+                        log_error("Too many arguments.");
+                        r = -EINVAL;
+                        goto finish;
                 }
 
-                if (r == 0)
-                        r = k;
+                r = show_statistics(bus);
+                break;
+
+        case MODE_RESET_STATISTICS:
+                if (argc > optind) {
+                        log_error("Too many arguments.");
+                        r = -EINVAL;
+                        goto finish;
+                }
 
-                optind++;
+                r = reset_statistics(bus);
+                break;
         }
 
 finish:
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
new file mode 100644 (file)
index 0000000..22004a0
--- /dev/null
@@ -0,0 +1,59 @@
+Y = Comprehensively Implemented, to the point appropriate for resolved
+D = Comprehensively Implemented, by a dependency of resolved
+! = Missing and something we might want to implement
+~ = Needs no explicit support or doesn't apply
+? = Is this relevant today?
+  = We are working on this
+
+Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
+Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
+Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support
+~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes
+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
+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
+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
+~ https://tools.ietf.org/html/rfc4470 → Minimally Covering NSEC Records and DNSSEC On-line Signing
+Y https://tools.ietf.org/html/rfc4501 → Domain Name System Uniform Resource Identifiers
+Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs)
+~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System
+~ 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
+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
+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
+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)
+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
+
+Also relevant:
+
+  https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/
index cc52ef9abe0ae76f75a28f922c23df6c64be65bc..058d14009abff7310ca2e35f7c8e81c7cebcd674 100644 (file)
@@ -77,7 +77,13 @@ bool dns_type_is_valid_query(uint16_t type) {
                        0,
                        DNS_TYPE_OPT,
                        DNS_TYPE_TSIG,
-                       DNS_TYPE_TKEY);
+                       DNS_TYPE_TKEY,
+
+                       /* RRSIG are technically valid as questions, but we refuse doing explicit queries for them, as
+                        * they aren't really payload, but signatures for payload, and cannot be validated on their
+                        * own. After all they are the signatures, and have no signatures of their own validating
+                        * them. */
+                       DNS_TYPE_RRSIG);
 }
 
 bool dns_type_is_valid_rr(uint16_t type) {
@@ -95,6 +101,88 @@ bool dns_class_is_valid_rr(uint16_t class) {
         return class != DNS_CLASS_ANY;
 }
 
+bool dns_type_may_redirect(uint16_t type) {
+        /* The following record types should never be redirected using
+         * CNAME/DNAME RRs. See
+         * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
+
+        if (dns_type_is_pseudo(type))
+                return false;
+
+        return !IN_SET(type,
+                       DNS_TYPE_CNAME,
+                       DNS_TYPE_DNAME,
+                       DNS_TYPE_NSEC3,
+                       DNS_TYPE_NSEC,
+                       DNS_TYPE_RRSIG,
+                       DNS_TYPE_NXT,
+                       DNS_TYPE_SIG,
+                       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,
+                      DNS_TYPE_DNSKEY,
+                      DNS_TYPE_RRSIG,
+                      DNS_TYPE_NSEC,
+                      DNS_TYPE_NSEC3,
+                      DNS_TYPE_NSEC3PARAM);
+}
+
+bool dns_type_is_obsolete(uint16_t type) {
+        return IN_SET(type,
+                      /* Obsoleted by RFC 973 */
+                      DNS_TYPE_MD,
+                      DNS_TYPE_MF,
+                      DNS_TYPE_MAILA,
+
+                      /* Kinda obsoleted by RFC 2505 */
+                      DNS_TYPE_MB,
+                      DNS_TYPE_MG,
+                      DNS_TYPE_MR,
+                      DNS_TYPE_MINFO,
+                      DNS_TYPE_MAILB,
+
+                      /* RFC1127 kinda obsoleted this by recommending against its use */
+                      DNS_TYPE_WKS,
+
+                      /* Declared historical by RFC 6563 */
+                      DNS_TYPE_A6,
+
+                      /* Obsoleted by DNSSEC-bis */
+                      DNS_TYPE_NXT,
+
+                      /* RFC 1035 removed support for concepts that needed this from RFC 883 */
+                      DNS_TYPE_NULL);
+}
+
 const char *dns_class_to_string(uint16_t class) {
 
         switch (class) {
index bea0adaa16f282c4df19e1f72d8eb1e477a7b260..78ff71b06e2f77eb04792defffa802fc44e67264 100644 (file)
@@ -128,6 +128,11 @@ enum {
 bool dns_type_is_pseudo(uint16_t type);
 bool dns_type_is_valid_query(uint16_t type);
 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);
 
 bool dns_class_is_pseudo(uint16_t class);
 bool dns_class_is_valid_rr(uint16_t class);
index af08a0555dc404a60f320d8b6368a0861ec938b2..9110ea52a610a19367b25aed4d94ca43c2ccab46 100644 (file)
 #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) {
 
@@ -61,14 +50,20 @@ static int reply_query_state(DnsQuery *q) {
                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
 
         case DNS_TRANSACTION_DNSSEC_FAILED:
-                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "DNSSEC validation failed: %s",
+                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s",
                                                   dnssec_result_to_string(q->answer_dnssec_result));
 
+        case DNS_TRANSACTION_NO_TRUST_ANCHOR:
+                return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known");
+
+        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_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 */
@@ -80,7 +75,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);
@@ -88,6 +83,7 @@ static int reply_query_state(DnsQuery *q) {
 
         case DNS_TRANSACTION_NULL:
         case DNS_TRANSACTION_PENDING:
+        case DNS_TRANSACTION_VALIDATING:
         case DNS_TRANSACTION_SUCCESS:
         default:
                 assert_not_reached("Impossible state");
@@ -149,12 +145,12 @@ 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)
                 goto finish;
-        if (r > 0) /* This was a cname, and the query was restarted. */
+        if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
                 return;
 
         r = sd_bus_message_new_method_return(q->request, &reply);
@@ -170,7 +166,11 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
                 int ifindex;
 
                 DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
-                        r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+                        DnsQuestion *question;
+
+                        question = dns_query_question_for_protocol(q, q->answer_protocol);
+
+                        r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
                         if (r < 0)
                                 goto finish;
                         if (r == 0)
@@ -188,7 +188,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
         }
 
         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;
         }
 
@@ -232,7 +232,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;
@@ -262,11 +262,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;
 
@@ -291,6 +295,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;
@@ -304,12 +309,12 @@ 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)
                 goto finish;
-        if (r > 0) /* This was a cname, and the query was restarted. */
+        if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
                 return;
 
         r = sd_bus_message_new_method_return(q->request, &reply);
@@ -320,20 +325,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) {
@@ -404,7 +409,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;
 
@@ -458,7 +463,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);
@@ -470,12 +478,12 @@ 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)
                 goto finish;
-        if (r > 0) /* Following a CNAME */
+        if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
                 return;
 
         r = sd_bus_message_new_method_return(q->request, &reply);
@@ -486,27 +494,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;
         }
 
@@ -555,7 +560,9 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
 
         if (!dns_type_is_valid_query(type))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid RR type for query %" PRIu16, type);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
+        if (dns_type_is_obsolete(type))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
 
         r = check_ifindex_flags(ifindex, &flags, 0, error);
         if (r < 0)
@@ -573,7 +580,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;
 
@@ -613,13 +620,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)
@@ -627,7 +637,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)
@@ -664,6 +674,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)
@@ -671,7 +682,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)
@@ -679,7 +692,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)
@@ -737,8 +750,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);
@@ -780,7 +795,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;
                                 }
 
@@ -801,31 +816,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;
         }
 
@@ -837,20 +849,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);
@@ -893,7 +901,7 @@ static void resolve_service_hostname_complete(DnsQuery *q) {
         }
 
         r = dns_query_process_cname(q);
-        if (r > 0) /* This was a cname, and the query was restarted. */
+        if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
                 return;
 
         /* This auxiliary lookup is finished or failed, let's see if all are finished now. */
@@ -914,11 +922,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;
 
@@ -952,8 +960,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);
 
@@ -964,43 +975,56 @@ 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)
                 goto finish;
-        if (r > 0) /* This was a cname, and the query was restarted. */
+        if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
                 return;
 
-        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;
 
-                        if (rr->key->type != DNS_TYPE_SRV)
-                                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;
                 }
+
+                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;
         }
 
@@ -1018,8 +1042,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;
@@ -1041,10 +1065,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;
@@ -1064,23 +1086,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;
 
@@ -1103,17 +1117,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;
 
@@ -1148,7 +1168,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;
 
@@ -1157,7 +1177,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++;
@@ -1166,7 +1186,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;
                 }
@@ -1214,16 +1234,238 @@ static int bus_property_get_search_domains(
         return sd_bus_message_close_container(reply);
 }
 
+static int bus_property_get_transaction_statistics(
+                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, "(tt)",
+                                     (uint64_t) hashmap_size(m->dns_transactions),
+                                     (uint64_t) m->n_transactions_total);
+}
+
+static int bus_property_get_cache_statistics(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t size = 0, hit = 0, miss = 0;
+        Manager *m = userdata;
+        DnsScope *s;
+
+        assert(reply);
+        assert(m);
+
+        LIST_FOREACH(scopes, s, m->dns_scopes) {
+                size += dns_cache_size(&s->cache);
+                hit += s->cache.n_hit;
+                miss += s->cache.n_miss;
+        }
+
+        return sd_bus_message_append(reply, "(ttt)", size, hit, miss);
+}
+
+static int bus_property_get_dnssec_statistics(
+                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, "(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);
+}
+
+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) {
+        Manager *m = userdata;
+        DnsScope *s;
+
+        assert(message);
+        assert(m);
+
+        LIST_FOREACH(scopes, s, m->dns_scopes)
+                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;
+
+        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("Domains", "a(is)", 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", "ias", 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,
 };
 
@@ -1281,6 +1523,7 @@ int manager_connect_bus(Manager *m) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to install bus reconnect time event: %m");
 
+                (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry");
                 return 0;
         }
 
@@ -1288,6 +1531,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");
index 1e728911789f0d1bef04f358402c3fb2c7ae37bd..1ee57ba43d51dd35b6603eb06c02780c073736e4 100644 (file)
@@ -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);
index 1b2f3e336e6b401b9d90cea68e24dafc7958f0da..88df7534c4bc4b9d84d966736b403c3b498ddabd 100644 (file)
@@ -200,75 +200,6 @@ int config_parse_search_domains(
         return 0;
 }
 
-int config_parse_support(
-                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) {
-
-        Support support, *v = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        support = support_from_string(rvalue);
-        if (support < 0) {
-                r = parse_boolean(rvalue);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse support level '%s'. Ignoring.", rvalue);
-                        return 0;
-                }
-
-                support = r ? SUPPORT_YES : SUPPORT_NO;
-        }
-
-        *v = support;
-        return 0;
-}
-
-int config_parse_dnssec(
-                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) {
-
-        Manager *m = data;
-        DnssecMode mode;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        mode = dnssec_mode_from_string(rvalue);
-        if (mode < 0) {
-                r = parse_boolean(rvalue);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNSSEC mode '%s'. Ignoring.", rvalue);
-                        return 0;
-                }
-
-                mode = r ? DNSSEC_YES : DNSSEC_NO;
-        }
-
-        m->unicast_scope->dnssec_mode = mode;
-        return 0;
-}
-
 int manager_parse_config_file(Manager *m) {
         int r;
 
index 668ea02bba878493f25c8fc4bf0cd66a79a94cf1..b4ef1b0378746370a5fc3b43a796e1ede4a1759f 100644 (file)
@@ -35,5 +35,4 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len
 
 int config_parse_dns_servers(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_search_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);
-int config_parse_support(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(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);
index 70577453e88298ce27a2e21434f53ba6031b6e1b..f74e44053104bfdeec85426885f689552ae57d03 100644 (file)
@@ -125,10 +125,13 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
                 if (r < 0)
                         return r;
                 if (r > 0) {
-                        /* Entry already exists, keep the entry with
-                         * the higher RR, or the one with TTL 0 */
+                        /* Don't mix contradicting TTLs (see below) */
+                        if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+                                return -EINVAL;
 
-                        if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
+                        /* Entry already exists, keep the entry with
+                         * the higher RR. */
+                        if (rr->ttl > a->items[i].rr->ttl) {
                                 dns_resource_record_ref(rr);
                                 dns_resource_record_unref(a->items[i].rr);
                                 a->items[i].rr = rr;
@@ -137,6 +140,21 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
                         a->items[i].flags |= flags;
                         return 0;
                 }
+
+                r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        /* There's already an RR of the same RRset in
+                         * place! Let's see if the TTLs more or less
+                         * match. We don't really care if they match
+                         * precisely, but we do care whether one is 0
+                         * and the other is not. See RFC 2181, Section
+                         * 5.2.*/
+
+                        if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+                                return -EINVAL;
+                }
         }
 
         return dns_answer_add_raw(a, rr, ifindex, flags);
@@ -302,9 +320,36 @@ int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
         return false;
 }
 
-int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
+int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
         DnsResourceRecord *rr;
-        DnsAnswerFlags rr_flags;
+        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;
         int r;
 
         assert(key);
@@ -318,15 +363,29 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco
                 if (r < 0)
                         return r;
                 if (r > 0) {
-                        if (ret)
-                                *ret = rr;
-                        if (flags)
-                                *flags = rr_flags;
-                        return 1;
+
+                        if (soa) {
+                                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
+                                if (r < 0)
+                                        return r;
+                                if (r > 0)
+                                        continue;
+                        }
+
+                        soa = rr;
+                        soa_flags = rr_flags;
                 }
         }
 
-        return 0;
+        if (!soa)
+                return 0;
+
+        if (ret)
+                *ret = soa;
+        if (flags)
+                *flags = soa_flags;
+
+        return 1;
 }
 
 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
@@ -337,7 +396,7 @@ int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsR
         assert(key);
 
         /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
-        if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
+        if (!dns_type_may_redirect(key->type))
                 return 0;
 
         DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
@@ -492,6 +551,92 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
         return 1;
 }
 
+int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
+        bool found = false, other = false;
+        DnsResourceRecord *rr;
+        unsigned i;
+        int r;
+
+        assert(a);
+        assert(rm);
+
+        /* Remove all entries matching the specified RR from *a */
+
+        DNS_ANSWER_FOREACH(rr, *a) {
+                r = dns_resource_record_equal(rr, rm);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        found = true;
+                else
+                        other = true;
+
+                if (found && other)
+                        break;
+        }
+
+        if (!found)
+                return 0;
+
+        if (!other) {
+                *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
+                return 1;
+        }
+
+        if ((*a)->n_ref > 1) {
+                _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
+                DnsAnswerFlags flags;
+                int ifindex;
+
+                copy = dns_answer_new((*a)->n_rrs);
+                if (!copy)
+                        return -ENOMEM;
+
+                DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
+                        r = dns_resource_record_equal(rr, rm);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
+                        r = dns_answer_add_raw(copy, rr, ifindex, flags);
+                        if (r < 0)
+                                return r;
+                }
+
+                dns_answer_unref(*a);
+                *a = copy;
+                copy = NULL;
+
+                return 1;
+        }
+
+        /* Only a single reference, edit in-place */
+
+        i = 0;
+        for (;;) {
+                if (i >= (*a)->n_rrs)
+                        break;
+
+                r = dns_resource_record_equal((*a)->items[i].rr, rm);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        /* Kill this entry */
+
+                        dns_resource_record_unref((*a)->items[i].rr);
+                        memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
+                        (*a)->n_rrs --;
+                        continue;
+
+                } else
+                        /* Keep this entry */
+                        i++;
+        }
+
+        return 1;
+}
+
 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
         DnsResourceRecord *rr_source;
         int ifindex_source, r;
@@ -643,18 +788,18 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
         DnsResourceRecord *rr;
         DnsAnswerFlags flags;
-        int ifindex, r;
+        int ifindex;
 
         if (!f)
                 f = stdout;
 
         DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
-                _cleanup_free_ char *t = NULL;
+                const char *t;
 
                 fputc('\t', f);
 
-                r = dns_resource_record_to_string(rr, &t);
-                if (r < 0) {
+                t = dns_resource_record_to_string(rr);
+                if (!t) {
                         log_oom();
                         continue;
                 }
@@ -676,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;
+}
index 28ded3b252b719e679b31ea0e9fd7f87e1c0a055..1875fd61366bde4a8816f7aeeddfab2ad3d6f586 100644 (file)
@@ -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);
@@ -77,9 +78,13 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
 int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free);
 
 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key);
+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;
 }
index f50d780ebbb9c99374031ac20e624f1cf5948d34..fdb34d11df487c93a0615488a5c46ea4416c37bf 100644 (file)
@@ -26,7 +26,8 @@
 #include "resolved-dns-packet.h"
 #include "string-util.h"
 
-/* Never cache more than 4K entries */
+/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
+ * leave DNS caches unbounded, but that's crazy. */
 #define CACHE_MAX 4096
 
 /* We never keep any item longer than 2h in our cache */
@@ -246,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 {
@@ -272,6 +286,42 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
         return NULL;
 }
 
+static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
+        uint32_t ttl;
+        usec_t u;
+
+        assert(rr);
+
+        ttl = MIN(rr->ttl, nsec_ttl);
+        if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
+                /* If this is a SOA RR, and it is requested, clamp to
+                 * the SOA's minimum field. This is used when we do
+                 * negative caching, to determine the TTL for the
+                 * negative caching entry.  See RFC 2308, Section
+                 * 5. */
+
+                if (ttl > rr->soa.minimum)
+                        ttl = rr->soa.minimum;
+        }
+
+        u = ttl * USEC_PER_SEC;
+        if (u > CACHE_TTL_MAX_USEC)
+                u = CACHE_TTL_MAX_USEC;
+
+        if (rr->expiry != USEC_INFINITY) {
+                usec_t left;
+
+                /* Make use of the DNSSEC RRSIG expiry info, if we
+                 * have it */
+
+                left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
+                if (u > left)
+                        u = left;
+        }
+
+        return timestamp + u;
+}
+
 static void dns_cache_item_update_positive(
                 DnsCache *c,
                 DnsCacheItem *i,
@@ -302,7 +352,7 @@ static void dns_cache_item_update_positive(
         dns_resource_key_unref(i->key);
         i->key = dns_resource_key_ref(rr->key);
 
-        i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+        i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
         i->authenticated = authenticated;
         i->shared_owner = shared_owner;
 
@@ -383,7 +433,7 @@ static int dns_cache_put_positive(
         i->type = DNS_CACHE_POSITIVE;
         i->key = dns_resource_key_ref(rr->key);
         i->rr = dns_resource_record_ref(rr);
-        i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+        i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
         i->authenticated = authenticated;
         i->shared_owner = shared_owner;
         i->owner_family = owner_family;
@@ -411,8 +461,9 @@ static int dns_cache_put_negative(
                 DnsResourceKey *key,
                 int rcode,
                 bool authenticated,
+                uint32_t nsec_ttl,
                 usec_t timestamp,
-                uint32_t soa_ttl,
+                DnsResourceRecord *soa,
                 int owner_family,
                 const union in_addr_union *owner_address) {
 
@@ -422,6 +473,7 @@ static int dns_cache_put_negative(
 
         assert(c);
         assert(key);
+        assert(soa);
         assert(owner_address);
 
         /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
@@ -432,13 +484,13 @@ static int dns_cache_put_negative(
         if (dns_type_is_pseudo(key->type))
                 return 0;
 
-        if (soa_ttl <= 0) {
+        if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
                 if (log_get_max_level() >= LOG_DEBUG) {
                         r = dns_resource_key_to_string(key, &key_str);
                         if (r < 0)
                                 return r;
 
-                        log_debug("Not caching negative entry with zero SOA TTL: %s", key_str);
+                        log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str);
                 }
 
                 return 0;
@@ -458,7 +510,7 @@ static int dns_cache_put_negative(
                 return -ENOMEM;
 
         i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
-        i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+        i->until = calculate_until(soa, nsec_ttl, timestamp, true);
         i->authenticated = authenticated;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
@@ -470,6 +522,14 @@ static int dns_cache_put_negative(
                 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
                 if (!i->key)
                         return -ENOMEM;
+
+                /* Make sure to remove any previous entry for this
+                 * specific ANY key. (For non-ANY keys the cache data
+                 * is already cleared by the caller.) Note that we
+                 * don't bother removing positive or NODATA cache
+                 * items in this case, because it would either be slow
+                 * or require explicit indexing by name */
+                dns_cache_remove_by_key(c, key);
         } else
                 i->key = dns_resource_key_ref(key);
 
@@ -525,6 +585,7 @@ int dns_cache_put(
                 int rcode,
                 DnsAnswer *answer,
                 bool authenticated,
+                uint32_t nsec_ttl,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
@@ -607,7 +668,6 @@ int dns_cache_put(
         /* See https://tools.ietf.org/html/rfc2308, which say that a
          * matching SOA record in the packet is used to to enable
          * negative caching. */
-
         r = dns_answer_find_soa(answer, key, &soa, &flags);
         if (r < 0)
                 goto fail;
@@ -624,8 +684,9 @@ int dns_cache_put(
                         key,
                         rcode,
                         authenticated,
+                        nsec_ttl,
                         timestamp,
-                        MIN(soa->soa.minimum, soa->ttl),
+                        soa,
                         owner_family, owner_address);
         if (r < 0)
                 goto fail;
@@ -672,11 +733,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
         if (i && i->type == DNS_CACHE_NXDOMAIN)
                 return i;
 
-        /* The following record types should never be redirected. See
-         * <https://tools.ietf.org/html/rfc4035#section-2.5>. */
-        if (!IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME,
-                            DNS_TYPE_NSEC3, DNS_TYPE_NSEC, DNS_TYPE_RRSIG,
-                            DNS_TYPE_NXT, DNS_TYPE_SIG, DNS_TYPE_KEY)) {
+        if (dns_type_may_redirect(k->type)) {
                 /* Check if we have a CNAME record instead */
                 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
                 if (i)
@@ -737,6 +794,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                         log_debug("Ignoring cache for ANY lookup: %s", key_str);
                 }
 
+                c->n_miss++;
+
                 *ret = NULL;
                 *rcode = DNS_RCODE_SUCCESS;
                 return 0;
@@ -754,6 +813,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                         log_debug("Cache miss for %s", key_str);
                 }
 
+                c->n_miss++;
+
                 *ret = NULL;
                 *rcode = DNS_RCODE_SUCCESS;
                 return 0;
@@ -791,9 +852,15 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                 *rcode = DNS_RCODE_SUCCESS;
                 *authenticated = nsec->authenticated;
 
-                return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
-                       !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
-                       !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
+                if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
+                    !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
+                    !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
+                        c->n_hit++;
+                        return 1;
+                }
+
+                c->n_miss++;
+                return 0;
         }
 
         if (log_get_max_level() >= LOG_DEBUG) {
@@ -808,6 +875,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
         }
 
         if (n <= 0) {
+                c->n_hit++;
+
                 *ret = NULL;
                 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
                 *authenticated = have_authenticated && !have_non_authenticated;
@@ -827,6 +896,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                         return r;
         }
 
+        c->n_hit++;
+
         *ret = answer;
         *rcode = DNS_RCODE_SUCCESS;
         *authenticated = have_authenticated && !have_non_authenticated;
@@ -935,13 +1006,13 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
                 DnsCacheItem *j;
 
                 LIST_FOREACH(by_key, j, i) {
-                        _cleanup_free_ char *t = NULL;
 
                         fputc('\t', f);
 
                         if (j->rr) {
-                                r = dns_resource_record_to_string(j->rr, &t);
-                                if (r < 0) {
+                                const char *t;
+                                t = dns_resource_record_to_string(j->rr);
+                                if (!t) {
                                         log_oom();
                                         continue;
                                 }
@@ -949,13 +1020,14 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
                                 fputs(t, f);
                                 fputc('\n', f);
                         } else {
-                                r = dns_resource_key_to_string(j->key, &t);
+                                _cleanup_free_ char *z = NULL;
+                                r = dns_resource_key_to_string(j->key, &z);
                                 if (r < 0) {
                                         log_oom();
                                         continue;
                                 }
 
-                                fputs(t, f);
+                                fputs(z, f);
                                 fputs(" -- ", f);
                                 fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
                                 fputc('\n', f);
@@ -970,3 +1042,10 @@ bool dns_cache_is_empty(DnsCache *cache) {
 
         return hashmap_isempty(cache->by_key);
 }
+
+unsigned dns_cache_size(DnsCache *cache) {
+        if (!cache)
+                return 0;
+
+        return hashmap_size(cache->by_key);
+}
index 856c975299ed13ac6319802285075a3c19ab2f67..e61b285df42dfabf276d53fd11c9a34fe0a91ae0 100644 (file)
@@ -29,6 +29,8 @@
 typedef struct DnsCache {
         Hashmap *by_key;
         Prioq *by_expiry;
+        unsigned n_hit;
+        unsigned n_miss;
 } DnsCache;
 
 #include "resolved-dns-answer.h"
@@ -39,7 +41,7 @@ typedef struct DnsCache {
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
 
-int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
+int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
 int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
 
 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
@@ -47,4 +49,6 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
 void dns_cache_dump(DnsCache *cache, FILE *f);
 bool dns_cache_is_empty(DnsCache *cache);
 
+unsigned dns_cache_size(DnsCache *cache);
+
 int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
index 814cb1c0f9e393665dee1d9197bf44fddc246ad9..1f48f588ce6c4f324d258d47b527b94ea9277904 100644 (file)
  *
  * TODO:
  *
- *   - Make trust anchor store read additional DS+DNSKEY data from disk
- *   - wildcard zones compatibility
- *   - multi-label zone compatibility
- *   - cname/dname compatibility
- *   - per-interface DNSSEC setting
- *   - fix TTL for cache entries to match RRSIG TTL
- *   - retry on failed validation?
- *   - DSA support
- *   - EC support?
- *
+ *   - bus calls to override DNSEC setting per interface
+ *   - log all DNSSEC downgrades
+ *   - log all RRs that failed validation
+ *   - enable by default
+ *   - Allow clients to request DNSSEC even if DNSSEC is off
+ *   - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet
  * */
 
 #define VERIFY_RRS_MAX 256
@@ -53,6 +49,9 @@
 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
 
+/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
+#define NSEC3_ITERATIONS_MAX 2500
+
 /*
  * The DNSSEC Chain of trust:
  *
@@ -77,17 +76,9 @@ static void initialize_libgcrypt(void) {
         gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
 }
 
-static bool dnssec_algorithm_supported(int algorithm) {
-        return IN_SET(algorithm,
-                      DNSSEC_ALGORITHM_RSASHA1,
-                      DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
-                      DNSSEC_ALGORITHM_RSASHA256,
-                      DNSSEC_ALGORITHM_RSASHA512);
-}
-
-uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
+uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
         const uint8_t *p;
-        uint32_t sum;
+        uint32_t sum, f;
         size_t i;
 
         /* The algorithm from RFC 4034, Appendix B. */
@@ -95,8 +86,12 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
         assert(dnskey);
         assert(dnskey->key->type == DNS_TYPE_DNSKEY);
 
-        sum = (uint32_t) dnskey->dnskey.flags +
-                ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
+        f = (uint32_t) dnskey->dnskey.flags;
+
+        if (mask_revoke)
+                f &= ~DNSKEY_FLAG_REVOKE;
+
+        sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
 
         p = dnskey->dnskey.key;
 
@@ -122,21 +117,21 @@ static int rr_compare(const void *a, const void *b) {
         assert(*y);
         assert((*y)->wire_format);
 
-        m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
+        m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
 
-        r = memcmp((*x)->wire_format, (*y)->wire_format, m);
+        r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
         if (r != 0)
                 return r;
 
-        if ((*x)->wire_format_size < (*y)->wire_format_size)
+        if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
                 return -1;
-        else if ((*x)->wire_format_size > (*y)->wire_format_size)
+        else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
                 return 1;
 
         return 0;
 }
 
-static int dnssec_rsa_verify(
+static int dnssec_rsa_verify_raw(
                 const char *hash_algorithm,
                 const void *signature, size_t signature_size,
                 const void *data, size_t data_size,
@@ -226,6 +221,196 @@ finish:
         return r;
 }
 
+static int dnssec_rsa_verify(
+                const char *hash_algorithm,
+                const void *hash, size_t hash_size,
+                DnsResourceRecord *rrsig,
+                DnsResourceRecord *dnskey) {
+
+        size_t exponent_size, modulus_size;
+        void *exponent, *modulus;
+
+        assert(hash_algorithm);
+        assert(hash);
+        assert(hash_size > 0);
+        assert(rrsig);
+        assert(dnskey);
+
+        if (*(uint8_t*) dnskey->dnskey.key == 0) {
+                /* exponent is > 255 bytes long */
+
+                exponent = (uint8_t*) dnskey->dnskey.key + 3;
+                exponent_size =
+                        ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
+                        ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
+
+                if (exponent_size < 256)
+                        return -EINVAL;
+
+                if (3 + exponent_size >= dnskey->dnskey.key_size)
+                        return -EINVAL;
+
+                modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
+                modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
+
+        } else {
+                /* exponent is <= 255 bytes long */
+
+                exponent = (uint8_t*) dnskey->dnskey.key + 1;
+                exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
+
+                if (exponent_size <= 0)
+                        return -EINVAL;
+
+                if (1 + exponent_size >= dnskey->dnskey.key_size)
+                        return -EINVAL;
+
+                modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
+                modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
+        }
+
+        return dnssec_rsa_verify_raw(
+                        hash_algorithm,
+                        rrsig->rrsig.signature, rrsig->rrsig.signature_size,
+                        hash, hash_size,
+                        exponent, exponent_size,
+                        modulus, modulus_size);
+}
+
+static int dnssec_ecdsa_verify_raw(
+                const char *hash_algorithm,
+                const char *curve,
+                const void *signature_r, size_t signature_r_size,
+                const void *signature_s, size_t signature_s_size,
+                const void *data, size_t data_size,
+                const void *key, size_t key_size) {
+
+        gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
+        gcry_mpi_t q = NULL, r = NULL, s = NULL;
+        gcry_error_t ge;
+        int k;
+
+        assert(hash_algorithm);
+
+        ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_sexp_build(&signature_sexp,
+                             NULL,
+                             "(sig-val (ecdsa (r %m) (s %m)))",
+                             r,
+                             s);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_sexp_build(&data_sexp,
+                             NULL,
+                             "(data (flags rfc6979) (hash %s %b))",
+                             hash_algorithm,
+                             (int) data_size,
+                             data);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_sexp_build(&public_key_sexp,
+                             NULL,
+                             "(public-key (ecc (curve %s) (q %m)))",
+                             curve,
+                             q);
+        if (ge != 0) {
+                k = -EIO;
+                goto finish;
+        }
+
+        ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
+        if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
+                k = 0;
+        else if (ge != 0) {
+                log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
+                k = -EIO;
+        } else
+                k = 1;
+finish:
+        if (r)
+                gcry_mpi_release(r);
+        if (s)
+                gcry_mpi_release(s);
+        if (q)
+                gcry_mpi_release(q);
+
+        if (public_key_sexp)
+                gcry_sexp_release(public_key_sexp);
+        if (signature_sexp)
+                gcry_sexp_release(signature_sexp);
+        if (data_sexp)
+                gcry_sexp_release(data_sexp);
+
+        return k;
+}
+
+static int dnssec_ecdsa_verify(
+                const char *hash_algorithm,
+                int algorithm,
+                const void *hash, size_t hash_size,
+                DnsResourceRecord *rrsig,
+                DnsResourceRecord *dnskey) {
+
+        const char *curve;
+        size_t key_size;
+        uint8_t *q;
+
+        assert(hash);
+        assert(hash_size);
+        assert(rrsig);
+        assert(dnskey);
+
+        if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
+                key_size = 32;
+                curve = "NIST P-256";
+        } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
+                key_size = 48;
+                curve = "NIST P-384";
+        } else
+                return -EOPNOTSUPP;
+
+        if (dnskey->dnskey.key_size != key_size * 2)
+                return -EINVAL;
+
+        if (rrsig->rrsig.signature_size != key_size * 2)
+                return -EINVAL;
+
+        q = alloca(key_size*2 + 1);
+        q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
+        memcpy(q+1, dnskey->dnskey.key, key_size*2);
+
+        return dnssec_ecdsa_verify_raw(
+                        hash_algorithm,
+                        curve,
+                        rrsig->rrsig.signature, key_size,
+                        (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
+                        hash, hash_size,
+                        q, key_size*2+1);
+}
+
 static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
         gcry_md_write(md, &v, sizeof(v));
 }
@@ -240,6 +425,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;
 
@@ -252,8 +488,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
@@ -275,21 +512,85 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
         return realtime < inception || realtime > expiration;
 }
 
+static int algorithm_to_gcrypt_md(uint8_t algorithm) {
+
+        /* Translates a DNSSEC signature algorithm into a gcrypt
+         * digest identifier.
+         *
+         * Note that we implement all algorithms listed as "Must
+         * implement" and "Recommended to Implement" in RFC6944. We
+         * don't implement any algorithms that are listed as
+         * "Optional" or "Must Not Implement". Specifically, we do not
+         * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
+         * GOST-ECC. */
+
+        switch (algorithm) {
+
+        case DNSSEC_ALGORITHM_RSASHA1:
+        case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
+                return GCRY_MD_SHA1;
+
+        case DNSSEC_ALGORITHM_RSASHA256:
+        case DNSSEC_ALGORITHM_ECDSAP256SHA256:
+                return GCRY_MD_SHA256;
+
+        case DNSSEC_ALGORITHM_ECDSAP384SHA384:
+                return GCRY_MD_SHA384;
+
+        case DNSSEC_ALGORITHM_RSASHA512:
+                return GCRY_MD_SHA512;
+
+        default:
+                return -EOPNOTSUPP;
+        }
+}
+
+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,
-                DnsResourceKey *key,
+                const DnsResourceKey *key,
                 DnsResourceRecord *rrsig,
                 DnsResourceRecord *dnskey,
                 usec_t realtime,
                 DnssecResult *result) {
 
         uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
-        size_t exponent_size, modulus_size, hash_size;
-        void *exponent, *modulus, *hash;
         DnsResourceRecord **list, *rr;
+        const char *source, *name;
         gcry_md_hd_t md = NULL;
+        int r, md_algorithm;
         size_t k, n = 0;
-        int r;
+        size_t hash_size;
+        void *hash;
+        bool wildcard;
 
         assert(key);
         assert(rrsig);
@@ -302,13 +603,21 @@ int dnssec_verify_rrset(
          * using the signature "rrsig" and the key "dnskey". It's
          * assumed the RRSIG and DNSKEY match. */
 
-        if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
+        md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
+        if (md_algorithm == -EOPNOTSUPP) {
                 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
                 return 0;
         }
+        if (md_algorithm < 0)
+                return md_algorithm;
 
-        if (a->n_rrs > VERIFY_RRS_MAX)
-                return -E2BIG;
+        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)
@@ -318,8 +627,54 @@ 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 *, a->n_rrs);
+        list = newa(DnsResourceRecord *, dns_answer_size(a));
 
         DNS_ANSWER_FOREACH(rr, a) {
                 r = dns_resource_key_equal(key, rr->key);
@@ -334,6 +689,9 @@ int dnssec_verify_rrset(
                         return r;
 
                 list[n++] = rr;
+
+                if (n > VERIFY_RRS_MAX)
+                        return -E2BIG;
         }
 
         if (n <= 0)
@@ -342,31 +700,13 @@ int dnssec_verify_rrset(
         /* Bring the RRs into canonical order */
         qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
 
-        initialize_libgcrypt();
-
         /* OK, the RRs are now in canonical order. Let's calculate the digest */
-        switch (rrsig->rrsig.algorithm) {
-
-        case DNSSEC_ALGORITHM_RSASHA1:
-        case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
-                gcry_md_open(&md, GCRY_MD_SHA1, 0);
-                hash_size = 20;
-                break;
-
-        case DNSSEC_ALGORITHM_RSASHA256:
-                gcry_md_open(&md, GCRY_MD_SHA256, 0);
-                hash_size = 32;
-                break;
-
-        case DNSSEC_ALGORITHM_RSASHA512:
-                gcry_md_open(&md, GCRY_MD_SHA512, 0);
-                hash_size = 64;
-                break;
+        initialize_libgcrypt();
 
-        default:
-                assert_not_reached("Unknown digest");
-        }
+        hash_size = gcry_md_get_algo_dlen(md_algorithm);
+        assert(hash_size > 0);
 
+        gcry_md_open(&md, md_algorithm, 0);
         if (!md)
                 return -EIO;
 
@@ -383,25 +723,30 @@ 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++) {
                 size_t l;
+
                 rr = list[k];
 
-                r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
-                if (r < 0)
-                        goto finish;
+                /* 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);
                 gcry_md_write(md, wire_format_name, r);
 
                 md_add_uint16(md, rr->key->type);
                 md_add_uint16(md, rr->key->class);
                 md_add_uint32(md, rrsig->rrsig.original_ttl);
 
-                assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
-                l = rr->wire_format_size - rr->wire_format_rdata_offset;
+                l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
                 assert(l <= 0xFFFF);
 
                 md_add_uint16(md, (uint16_t) l);
-                gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
+                gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
         }
 
         hash = gcry_md_read(md, 0);
@@ -410,57 +755,44 @@ int dnssec_verify_rrset(
                 goto finish;
         }
 
-        if (*(uint8_t*) dnskey->dnskey.key == 0) {
-                /* exponent is > 255 bytes long */
+        switch (rrsig->rrsig.algorithm) {
 
-                exponent = (uint8_t*) dnskey->dnskey.key + 3;
-                exponent_size =
-                        ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
-                        ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
+        case DNSSEC_ALGORITHM_RSASHA1:
+        case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
+        case DNSSEC_ALGORITHM_RSASHA256:
+        case DNSSEC_ALGORITHM_RSASHA512:
+                r = dnssec_rsa_verify(
+                                gcry_md_algo_name(md_algorithm),
+                                hash, hash_size,
+                                rrsig,
+                                dnskey);
+                break;
 
-                if (exponent_size < 256) {
-                        r = -EINVAL;
-                        goto finish;
-                }
+        case DNSSEC_ALGORITHM_ECDSAP256SHA256:
+        case DNSSEC_ALGORITHM_ECDSAP384SHA384:
+                r = dnssec_ecdsa_verify(
+                                gcry_md_algo_name(md_algorithm),
+                                rrsig->rrsig.algorithm,
+                                hash, hash_size,
+                                rrsig,
+                                dnskey);
+                break;
+        }
 
-                if (3 + exponent_size >= dnskey->dnskey.key_size) {
-                        r = -EINVAL;
-                        goto finish;
-                }
-
-                modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
-                modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
-
-        } else {
-                /* exponent is <= 255 bytes long */
-
-                exponent = (uint8_t*) dnskey->dnskey.key + 1;
-                exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
-
-                if (exponent_size <= 0) {
-                        r = -EINVAL;
-                        goto finish;
-                }
-
-                if (1 + exponent_size >= dnskey->dnskey.key_size) {
-                        r = -EINVAL;
-                        goto finish;
-                }
-
-                modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
-                modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
-        }
-
-        r = dnssec_rsa_verify(
-                        gcry_md_algo_name(gcry_md_get_algo(md)),
-                        rrsig->rrsig.signature, rrsig->rrsig.signature_size,
-                        hash, hash_size,
-                        exponent, exponent_size,
-                        modulus, modulus_size);
         if (r < 0)
                 goto finish;
 
-        *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
+        /* 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:
@@ -468,7 +800,7 @@ finish:
         return r;
 }
 
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
 
         assert(rrsig);
         assert(dnskey);
@@ -485,12 +817,14 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
                 return 0;
         if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
                 return 0;
+        if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
+                return 0;
         if (dnskey->dnskey.protocol != 3)
                 return 0;
         if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
                 return 0;
 
-        if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
+        if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
                 return 0;
 
         return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
@@ -514,10 +848,11 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
 
 int dnssec_verify_rrset_search(
                 DnsAnswer *a,
-                DnsResourceKey *key,
+                const DnsResourceKey *key,
                 DnsAnswer *validated_dnskeys,
                 usec_t realtime,
-                DnssecResult *result) {
+                DnssecResult *result,
+                DnsResourceRecord **ret_rrsig) {
 
         bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
         DnsResourceRecord *rrsig;
@@ -553,7 +888,7 @@ int dnssec_verify_rrset_search(
                                 continue;
 
                         /* Is this a DNSKEY RR that matches they key of our RRSIG? */
-                        r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
+                        r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
                         if (r < 0)
                                 return r;
                         if (r == 0)
@@ -577,9 +912,13 @@ int dnssec_verify_rrset_search(
                         switch (one_result) {
 
                         case DNSSEC_VALIDATED:
+                        case DNSSEC_VALIDATED_WILDCARD:
                                 /* Yay, the RR has been validated,
-                                 * return immediately. */
-                                *result = DNSSEC_VALIDATED;
+                                 * return immediately, but fix up the expiry */
+                                if (ret_rrsig)
+                                        *ret_rrsig = rrsig;
+
+                                *result = one_result;
                                 return 0;
 
                         case DNSSEC_INVALID:
@@ -624,6 +963,9 @@ int dnssec_verify_rrset_search(
         else
                 *result = DNSSEC_NO_SIGNATURE;
 
+        if (ret_rrsig)
+                *ret_rrsig = NULL;
+
         return 0;
 }
 
@@ -655,23 +997,11 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
                 return -ENOBUFS;
 
         for (;;) {
-                size_t i;
-
                 r = dns_label_unescape(&n, buffer, buffer_max);
                 if (r < 0)
                         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;
@@ -683,11 +1013,7 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
                 if (memchr(buffer, '.', r))
                         return -EINVAL;
 
-                for (i = 0; i < (size_t) r; i ++) {
-                        if (buffer[i] >= 'A' && buffer[i] <= 'Z')
-                                buffer[i] = buffer[i] - 'A' + 'a';
-                }
-
+                ascii_strlower_n(buffer, (size_t) r);
                 buffer[r] = '.';
 
                 buffer += r + 1;
@@ -709,9 +1035,9 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
         return (int) c;
 }
 
-static int digest_to_gcrypt(uint8_t algorithm) {
+static int digest_to_gcrypt_md(uint8_t algorithm) {
 
-        /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
+        /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
 
         switch (algorithm) {
 
@@ -721,18 +1047,20 @@ static int digest_to_gcrypt(uint8_t algorithm) {
         case DNSSEC_DIGEST_SHA256:
                 return GCRY_MD_SHA256;
 
+        case DNSSEC_DIGEST_SHA384:
+                return GCRY_MD_SHA384;
+
         default:
                 return -EOPNOTSUPP;
         }
 }
 
-int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
+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;
-        int algorithm;
+        int md_algorithm, r;
         void *result;
-        int r;
 
         assert(dnskey);
         assert(ds);
@@ -745,21 +1073,23 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
                 return -EINVAL;
         if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
                 return -EKEYREJECTED;
+        if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
+                return -EKEYREJECTED;
         if (dnskey->dnskey.protocol != 3)
                 return -EKEYREJECTED;
 
         if (dnskey->dnskey.algorithm != ds->ds.algorithm)
                 return 0;
-        if (dnssec_keytag(dnskey) != ds->ds.key_tag)
+        if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
                 return 0;
 
         initialize_libgcrypt();
 
-        algorithm = digest_to_gcrypt(ds->ds.digest_type);
-        if (algorithm < 0)
-                return algorithm;
+        md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
+        if (md_algorithm < 0)
+                return md_algorithm;
 
-        hash_size = gcry_md_get_algo_dlen(algorithm);
+        hash_size = gcry_md_get_algo_dlen(md_algorithm);
         assert(hash_size > 0);
 
         if (ds->ds.digest_size != hash_size)
@@ -769,12 +1099,15 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
         if (r < 0)
                 return r;
 
-        gcry_md_open(&md, algorithm, 0);
+        gcry_md_open(&md, md_algorithm, 0);
         if (!md)
                 return -EIO;
 
         gcry_md_write(md, owner_name, r);
-        md_add_uint16(md, dnskey->dnskey.flags);
+        if (mask_revoke)
+                md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
+        else
+                md_add_uint16(md, dnskey->dnskey.flags);
         md_add_uint8(md, dnskey->dnskey.protocol);
         md_add_uint8(md, dnskey->dnskey.algorithm);
         gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
@@ -792,7 +1125,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;
@@ -809,8 +1142,18 @@ 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;
 
-                r = dnssec_verify_dnskey(dnskey, ds);
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                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)
@@ -820,6 +1163,20 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
         return 0;
 }
 
+static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
+
+        /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
+
+        switch (algorithm) {
+
+        case NSEC3_ALGORITHM_SHA1:
+                return GCRY_MD_SHA1;
+
+        default:
+                return -EOPNOTSUPP;
+        }
+}
+
 int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
         uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
         gcry_md_hd_t md = NULL;
@@ -836,7 +1193,12 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
         if (nsec3->key->type != DNS_TYPE_NSEC3)
                 return -EINVAL;
 
-        algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm);
+        if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
+                log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
+                return -EOPNOTSUPP;
+        }
+
+        algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
         if (algorithm < 0)
                 return algorithm;
 
@@ -888,62 +1250,192 @@ finish:
         return r;
 }
 
-static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
-        _cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
+static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
+        const char *a, *b;
+        int r;
+
+        assert(rr);
+
+        if (rr->key->type != DNS_TYPE_NSEC3)
+                return 0;
+
+        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
+        if (!IN_SET(rr->nsec3.flags, 0, 1))
+                return 0;
+
+        /* Ignore NSEC3 RRs whose algorithm we don't know */
+        if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
+                return 0;
+        /* Ignore NSEC3 RRs with an excessive number of required iterations */
+        if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
+                return 0;
+
+        /* Ignore NSEC3 RRs generated from wildcards */
+        if (rr->n_skip_labels_source != 0)
+                return 0;
+        /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
+        if (rr->n_skip_labels_signer != 1)
+                return 0;
+
+        if (!nsec3)
+                return 1;
+
+        /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
+
+        if (nsec3 == rr) /* Shortcut */
+                return 1;
+
+        if (rr->key->class != nsec3->key->class)
+                return 0;
+        if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
+                return 0;
+        if (rr->nsec3.iterations != nsec3->nsec3.iterations)
+                return 0;
+        if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
+                return 0;
+        if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
+                return 0;
+
+        a = DNS_RESOURCE_KEY_NAME(rr->key);
+        r = dns_name_parent(&a); /* strip off hash */
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 0;
+
+        b = DNS_RESOURCE_KEY_NAME(nsec3->key);
+        r = dns_name_parent(&b); /* strip off hash */
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 0;
+
+        /* Make sure both have the same parent */
+        return dns_name_equal(a, b);
+}
+
+static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
+        _cleanup_free_ char *l = NULL;
+        char *j;
+
+        assert(hashed);
+        assert(hashed_size > 0);
+        assert(zone);
+        assert(ret);
+
+        l = base32hexmem(hashed, hashed_size, false);
+        if (!l)
+                return -ENOMEM;
+
+        j = strjoin(l, ".", zone, NULL);
+        if (!j)
+                return -ENOMEM;
+
+        *ret = j;
+        return (int) hashed_size;
+}
+
+static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
         uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
-        const char *p, *pp = NULL;
-        DnsResourceRecord *rr;
+        int hashed_size;
+
+        assert(nsec3);
+        assert(domain);
+        assert(zone);
+        assert(ret);
+
+        hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
+        if (hashed_size < 0)
+                return hashed_size;
+
+        return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
+}
+
+/* See RFC 5155, Section 8
+ * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
+ * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
+ * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
+ * matches the wildcard domain.
+ *
+ * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
+ * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
+ * 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_domain = NULL;
+        const char *zone, *p, *pp = NULL, *wildcard;
+        DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
         DnsAnswerFlags flags;
         int hashed_size, r;
+        bool a, no_closer = false, no_wildcard = false, optout = false;
 
         assert(key);
         assert(result);
 
-        /* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
-        p = DNS_RESOURCE_KEY_NAME(key);
+        /* First step, find the zone name and the NSEC3 parameters of the zone.
+         * it is sufficient to look for the longest common suffix we find with
+         * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
+         * records from a given zone in a response must use the same
+         * parameters. */
+        zone = DNS_RESOURCE_KEY_NAME(key);
         for (;;) {
-                DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
-                        _cleanup_free_ char *hashed_domain = NULL, *label = NULL;
-
-                        if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+                DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
+                        r = nsec3_is_good(zone_rr, NULL);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
                                 continue;
 
-                        if (rr->key->type != DNS_TYPE_NSEC3)
-                                continue;
+                        r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                goto found_zone;
+                }
 
-                        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
-                        if (!IN_SET(rr->nsec3.flags, 0, 1))
-                                continue;
+                /* Strip one label from the front */
+                r = dns_name_parent(&zone);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+        }
+
+        *result = DNSSEC_NSEC_NO_RR;
+        return 0;
+
+found_zone:
+        /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
+        p = DNS_RESOURCE_KEY_NAME(key);
+        for (;;) {
+                _cleanup_free_ char *hashed_domain = NULL;
+
+                hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
+                if (hashed_size == -EOPNOTSUPP) {
+                        *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
+                        return 0;
+                }
+                if (hashed_size < 0)
+                        return hashed_size;
+
+                DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
 
-                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), p);
+                        r = nsec3_is_good(enclosure_rr, zone_rr);
                         if (r < 0)
                                 return r;
                         if (r == 0)
                                 continue;
 
-                        hashed_size = dnssec_nsec3_hash(rr, p, hashed);
-                        if (hashed_size == -EOPNOTSUPP) {
-                                *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
-                                return 0;
-                        }
-                        if (hashed_size < 0)
-                                return hashed_size;
-                        if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
-                                return -EBADMSG;
-
-                        label = base32hexmem(hashed, hashed_size, false);
-                        if (!label)
-                                return -ENOMEM;
-
-                        hashed_domain = strjoin(label, ".", p, NULL);
-                        if (!hashed_domain)
-                                return -ENOMEM;
+                        if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
+                                continue;
 
-                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
                         if (r < 0)
                                 return r;
-                        if (r > 0)
-                                goto found;
+                        if (r > 0) {
+                                a = flags & DNS_ANSWER_AUTHENTICATED;
+                                goto found_closest_encloser;
+                        }
                 }
 
                 /* We didn't find the closest encloser with this name,
@@ -963,97 +1455,305 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
         *result = DNSSEC_NSEC_NO_RR;
         return 0;
 
-found:
+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(rr->nsec3.types, DNS_TYPE_DNAME))
+        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(rr->nsec3.types, DNS_TYPE_NS) &&
-            !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA))
+        if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+            !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
                 return -EBADMSG;
 
         if (!pp) {
                 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
-                *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
+                if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
+                        *result = DNSSEC_NSEC_FOUND;
+                else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
+                        *result = DNSSEC_NSEC_CNAME;
+                else
+                        *result = DNSSEC_NSEC_NODATA;
+
+                if (authenticated)
+                        *authenticated = a;
+                if (ttl)
+                        *ttl = enclosure_rr->ttl;
+
                 return 0;
         }
 
-        r = dnssec_nsec3_hash(rr, pp, hashed);
+        /* 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;
         if (r != hashed_size)
                 return -EBADMSG;
 
-        l = base32hexmem(hashed, hashed_size, false);
-        if (!l)
-                return -ENOMEM;
-
-        next_closer_domain = strjoin(l, ".", p, NULL);
-        if (!next_closer_domain)
-                return -ENOMEM;
+        r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
+        if (r < 0)
+                return r;
+        if (r != hashed_size)
+                return -EBADMSG;
 
         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
-                _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
-                const char *nsec3_parent;
-
-                if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
-                        continue;
-
-                if (rr->key->type != DNS_TYPE_NSEC3)
-                        continue;
-
-                /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
-                if (!IN_SET(rr->nsec3.flags, 0, 1))
-                        continue;
+                _cleanup_free_ char *next_hashed_domain = NULL;
 
-                nsec3_parent = DNS_RESOURCE_KEY_NAME(rr->key);
-                r = dns_name_parent(&nsec3_parent);
+                r = nsec3_is_good(rr, zone_rr);
                 if (r < 0)
                         return r;
                 if (r == 0)
                         continue;
 
-                r = dns_name_equal(p, nsec3_parent);
+                r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        continue;
 
-                label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
-                if (!label)
-                        return -ENOMEM;
+                r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        if (rr->nsec3.flags & 1)
+                                optout = true;
 
-                next_hashed_domain = strjoin(label, ".", p, NULL);
-                if (!next_hashed_domain)
-                        return -ENOMEM;
+                        a = a && (flags & DNS_ANSWER_AUTHENTICATED);
 
-                r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
+                        no_closer = true;
+                }
+
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+                        wildcard_rr = rr;
+                }
+
+                r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
                 if (r < 0)
                         return r;
                 if (r > 0) {
                         if (rr->nsec3.flags & 1)
-                                *result = DNSSEC_NSEC_OPTOUT;
-                        else
-                                *result = DNSSEC_NSEC_NXDOMAIN;
+                                /* This only makes sense if we have a wildcard delegation, which is
+                                 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
+                                 * this not happening, so hence cannot simply conclude NXDOMAIN as
+                                 * we would wish */
+                                optout = true;
 
-                        return 1;
+                        a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+                        no_wildcard = true;
                 }
         }
 
-        *result = DNSSEC_NSEC_NO_RR;
+        if (wildcard_rr && no_wildcard)
+                return -EBADMSG;
+
+        if (!no_closer) {
+                *result = DNSSEC_NSEC_NO_RR;
+                return 0;
+        }
+
+        if (wildcard_rr) {
+                /* A wildcard exists that matches our query. */
+                if (optout)
+                        /* This is not specified in any RFC to the best of my knowledge, but
+                         * if the next closer enclosure is covered by an opt-out NSEC3 RR
+                         * it means that we cannot prove that the source of synthesis is
+                         * correct, as there may be a closer match. */
+                        *result = DNSSEC_NSEC_OPTOUT;
+                else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
+                        *result = DNSSEC_NSEC_FOUND;
+                else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
+                        *result = DNSSEC_NSEC_CNAME;
+                else
+                        *result = DNSSEC_NSEC_NODATA;
+        } else {
+                if (optout)
+                        /* The RFC only specifies that we have to care for optout for NODATA for
+                         * DS records. However, children of an insecure opt-out delegation should
+                         * also be considered opt-out, rather than verified NXDOMAIN.
+                         * Note that we do not require a proof of wildcard non-existence if the
+                         * next closer domain is covered by an opt-out, as that would not provide
+                         * any additional information. */
+                        *result = DNSSEC_NSEC_OPTOUT;
+                else if (no_wildcard)
+                        *result = DNSSEC_NSEC_NXDOMAIN;
+                else {
+                        *result = DNSSEC_NSEC_NO_RR;
+
+                        return 0;
+                }
+        }
+
+        if (authenticated)
+                *authenticated = a;
+
+        if (ttl)
+                *ttl = enclosure_rr->ttl;
+
         return 0;
 }
 
-int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
-        DnsResourceRecord *rr;
-        bool have_nsec3 = false;
+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) {
+        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);
@@ -1061,58 +1761,363 @@ int dnssec_test_nsec(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;
 
-                if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+                have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
+
+                if (rr->key->type != DNS_TYPE_NSEC)
+                        continue;
+
+                /* 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 (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;
+                        }
+
+                        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;
+                }
+
+                /* 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);
+
+        /* No approproate NSEC RR found, report this. */
+        *result = DNSSEC_NSEC_NO_RR;
+        return 0;
+}
+
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+        int r;
+
+        assert(name);
+        assert(zone);
+
+        /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
+         * 'zone'. The 'zone' must be a suffix of the 'name'. */
+
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+                bool found = false;
+
+                if (rr->key->type != type && type != DNS_TYPE_ANY)
                         continue;
 
                 switch (rr->key->type) {
 
                 case DNS_TYPE_NSEC:
 
-                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+                        /* 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) {
-                                *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
-                                return 0;
-                        }
+                        if (r == 0)
+                                continue;
 
-                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
+                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
                         if (r < 0)
                                 return r;
-                        if (r > 0) {
-                                *result = DNSSEC_NSEC_NXDOMAIN;
-                                return 0;
-                        }
+
+                        found = r > 0;
                         break;
 
-                case DNS_TYPE_NSEC3:
-                        have_nsec3 = true;
+                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;
+                        if (r == 0)
+                                break;
+
+                        /* Format the domain we are testing with the NSEC3 RR's hash function */
+                        r = nsec3_hashed_domain_make(
+                                        rr,
+                                        name,
+                                        zone,
+                                        &hashed_domain);
+                        if (r < 0)
+                                return r;
+                        if ((size_t) r != rr->nsec3.next_hashed_name_size)
+                                break;
+
+                        /* Format the NSEC3's next hashed name as proper domain name */
+                        r = nsec3_hashed_domain_format(
+                                        rr->nsec3.next_hashed_name,
+                                        rr->nsec3.next_hashed_name_size,
+                                        zone,
+                                        &next_hashed_domain);
+                        if (r < 0)
+                                return r;
+
+                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
+                        if (r < 0)
+                                return r;
+
+                        found = r > 0;
                         break;
                 }
-        }
 
-        /* OK, this was not sufficient. Let's see if NSEC3 can help. */
-        if (have_nsec3)
-                return dnssec_test_nsec3(answer, key, result);
+                default:
+                        continue;
+                }
+
+                if (found) {
+                        if (authenticated)
+                                *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+                        return 1;
+                }
+        }
 
-        /* No approproate NSEC RR found, report this. */
-        *result = DNSSEC_NSEC_NO_RR;
         return 0;
 }
 
-static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
-        [DNSSEC_NO] = "no",
-        [DNSSEC_YES] = "yes",
-};
-DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
+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",
         [DNSSEC_INVALID] = "invalid",
         [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
         [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
@@ -1121,5 +2126,6 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
         [DNSSEC_UNSIGNED] = "unsigned",
         [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
         [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
+        [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
 };
 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
index d17d5142f5a788cdbf4e29404c9f0424d95a4ecc..955017e8cb72fc435850d1df3fcf9de8d96ed701 100644 (file)
@@ -28,20 +28,10 @@ typedef enum DnssecResult DnssecResult;
 #include "resolved-dns-answer.h"
 #include "resolved-dns-rr.h"
 
-enum DnssecMode {
-        /* No DNSSEC validation is done */
-        DNSSEC_NO,
-
-        /* Validate locally, if the server knows DO, but if not, don't. Don't trust the AD bit */
-        DNSSEC_YES,
-
-        _DNSSEC_MODE_MAX,
-        _DNSSEC_MODE_INVALID = -1
-};
-
 enum DnssecResult {
-        /* These four are returned by dnssec_verify_rrset() */
+        /* These five are returned by dnssec_verify_rrset() */
         DNSSEC_VALIDATED,
+        DNSSEC_VALIDATED_WILDCARD, /* Validated via a wildcard RRSIG, further NSEC/NSEC3 checks necessary */
         DNSSEC_INVALID,
         DNSSEC_SIGNATURE_EXPIRED,
         DNSSEC_UNSUPPORTED_ALGORITHM,
@@ -54,6 +44,8 @@ enum DnssecResult {
         DNSSEC_UNSIGNED,
         DNSSEC_FAILED_AUXILIARY,
         DNSSEC_NSEC_MISMATCH,
+        DNSSEC_INCOMPATIBLE_SERVER,
+
         _DNSSEC_RESULT_MAX,
         _DNSSEC_RESULT_INVALID = -1
 };
@@ -63,18 +55,18 @@ enum DnssecResult {
 /* The longest digest we'll ever generate, of all digest algorithms we support */
 #define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
 
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey);
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok);
 int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
 
-int dnssec_verify_rrset(DnsAnswer *answer, DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
-int dnssec_verify_rrset_search(DnsAnswer *answer, DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result);
+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);
-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);
 
-uint16_t dnssec_keytag(DnsResourceRecord *dnskey);
+uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke);
 
 int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
 
@@ -82,6 +74,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret);
 
 typedef enum DnssecNsecResult {
         DNSSEC_NSEC_NO_RR,     /* No suitable NSEC/NSEC3 RR found */
+        DNSSEC_NSEC_CNAME,     /* Didn't find what was asked for, but did find CNAME */
         DNSSEC_NSEC_UNSUPPORTED_ALGORITHM,
         DNSSEC_NSEC_NXDOMAIN,
         DNSSEC_NSEC_NODATA,
@@ -89,10 +82,11 @@ typedef enum DnssecNsecResult {
         DNSSEC_NSEC_OPTOUT,
 } DnssecNsecResult;
 
-int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result);
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
+
+int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated);
 
-const char* dnssec_mode_to_string(DnssecMode m) _const_;
-DnssecMode dnssec_mode_from_string(const char *s) _pure_;
+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_;
index 14faf9e4abaa04d6829aa8865ad35415cc5a51f9..9a5223ef01295c74ebfe15d9be319cd7f2920908 100644 (file)
@@ -58,6 +58,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
         p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
         p->allocated = a;
         p->protocol = protocol;
+        p->opt_start = p->opt_size = (size_t) -1;
         p->n_ref = 1;
 
         *ret = p;
@@ -465,12 +466,8 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonica
                 /* Generate in canonical form, as defined by DNSSEC
                  * RFC 4034, Section 6.2, i.e. all lower-case. */
 
-                for (i = 0; i < l; i++) {
-                        if (d[i] >= 'A' && d[i] <= 'Z')
-                                w[i] = (uint8_t) (d[i] - 'A' + 'a');
-                        else
-                                w[i] = (uint8_t) d[i];
-                }
+                for (i = 0; i < l; i++)
+                        w[i] = (uint8_t) ascii_tolower(d[i]);
         } else
                 /* Otherwise, just copy the string unaltered. This is
                  * essential for DNS-SD, where the casing of labels
@@ -498,11 +495,10 @@ int dns_packet_append_name(
 
         saved_size = p->size;
 
-        while (*name) {
-                _cleanup_free_ char *s = NULL;
+        while (!dns_name_is_root(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));
@@ -518,32 +514,23 @@ int dns_packet_append_name(
                         }
                 }
 
-                s = strdup(name);
-                if (!s) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
                 r = dns_label_unescape(&name, label, sizeof(label));
                 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;
 
                 if (allow_compression) {
+                        _cleanup_free_ char *s = NULL;
+
+                        s = strdup(z);
+                        if (!s) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
                         r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops);
                         if (r < 0)
                                 goto fail;
@@ -643,7 +630,6 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
         int r;
 
         assert(p);
-        assert(types);
 
         saved_size = p->size;
 
@@ -680,7 +666,7 @@ fail:
 }
 
 /* Append the OPT pseudo-RR described in RFC6891 */
-int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
+int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
         size_t saved_size;
         int r;
 
@@ -688,6 +674,11 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
         /* we must never advertise supported packet size smaller than the legacy max */
         assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
 
+        if (p->opt_start != (size_t) -1)
+                return -EBUSY;
+
+        assert(p->opt_size == (size_t) -1);
+
         saved_size = p->size;
 
         /* empty name */
@@ -716,10 +707,48 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
                 goto fail;
 
         /* RDLENGTH */
-        r = dns_packet_append_uint16(p, 0, NULL);
+
+        if (edns0_do) {
+                /* If DO is on, also append RFC6975 Algorithm data */
+
+                static const uint8_t rfc6975[] = {
+
+                        0, 5, /* OPTION_CODE: DAU */
+                        0, 6, /* LIST_LENGTH */
+                        DNSSEC_ALGORITHM_RSASHA1,
+                        DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
+                        DNSSEC_ALGORITHM_RSASHA256,
+                        DNSSEC_ALGORITHM_RSASHA512,
+                        DNSSEC_ALGORITHM_ECDSAP256SHA256,
+                        DNSSEC_ALGORITHM_ECDSAP384SHA384,
+
+                        0, 6, /* OPTION_CODE: DHU */
+                        0, 3, /* LIST_LENGTH */
+                        DNSSEC_DIGEST_SHA1,
+                        DNSSEC_DIGEST_SHA256,
+                        DNSSEC_DIGEST_SHA384,
+
+                        0, 7, /* OPTION_CODE: N3U */
+                        0, 1, /* LIST_LENGTH */
+                        NSEC3_ALGORITHM_SHA1,
+                };
+
+                r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL);
+                if (r < 0)
+                        goto fail;
+
+                r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
+        } else
+                r = dns_packet_append_uint16(p, 0, NULL);
+
         if (r < 0)
                 goto fail;
 
+        DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) + 1);
+
+        p->opt_start = saved_size;
+        p->opt_size = p->size - saved_size;
+
         if (start)
                 *start = saved_size;
 
@@ -730,6 +759,27 @@ fail:
         return r;
 }
 
+int dns_packet_truncate_opt(DnsPacket *p) {
+        assert(p);
+
+        if (p->opt_start == (size_t) -1) {
+                assert(p->opt_size == (size_t) -1);
+                return 0;
+        }
+
+        assert(p->opt_size != (size_t) -1);
+        assert(DNS_PACKET_ARCOUNT(p) > 0);
+
+        if (p->opt_start + p->opt_size != p->size)
+                return -EBUSY;
+
+        dns_packet_truncate(p, p->opt_start);
+        DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) - 1);
+        p->opt_start = p->opt_size = (size_t) -1;
+
+        return 1;
+}
+
 int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
         size_t saved_size, rdlength_offset, end, rdlength, rds;
         int r;
@@ -1021,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;
         }
 
@@ -1045,7 +1095,6 @@ fail:
         return r;
 }
 
-
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
 
@@ -1548,6 +1597,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
         if (r < 0)
                 goto fail;
 
+        /* RFC 2181, Section 8, suggests to
+         * treat a TTL with the MSB set as a zero TTL. */
+        if (rr->ttl & UINT32_C(0x80000000))
+                rr->ttl = 0;
+
         r = dns_packet_read_uint16(p, &rdlength, NULL);
         if (r < 0)
                 goto fail;
@@ -1951,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;
@@ -1998,6 +2094,9 @@ int dns_packet_extract(DnsPacket *p) {
 
         n = DNS_PACKET_RRCOUNT(p);
         if (n > 0) {
+                DnsResourceRecord *previous = NULL;
+                bool bad_opt = false;
+
                 answer = dns_answer_new(n);
                 if (!answer) {
                         r = -ENOMEM;
@@ -2012,35 +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;
                                 }
 
-                                /* The OPT RR is only valid in the Additional section */
                                 if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) {
-                                        r = -EBADMSG;
-                                        goto finish;
+                                        /* 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;
                                 }
 
-                                /* Two OPT RRs? */
-                                if (p->opt) {
-                                        r = -EBADMSG;
-                                        goto finish;
+                                if (!opt_is_good(rr, &has_rfc6975)) {
+                                        log_debug("Malformed OPT RR, ignoring.");
+                                        bad_opt = true;
+                                        continue;
+                                }
+
+                                if (has_rfc6975) {
+                                        /* 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 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) |
@@ -2049,6 +2175,9 @@ int dns_packet_extract(DnsPacket *p) {
                                         goto finish;
                         }
                 }
+
+                if (bad_opt)
+                        p->opt = dns_resource_record_unref(p->opt);
         }
 
         p->question = question;
index 36da86dee5590acabd521d53b09ccd99bbf09bb0..c53431576b82ee6b5aa97fa7c95739ef841d87a6 100644 (file)
@@ -76,6 +76,7 @@ struct DnsPacket {
         size_t size, allocated, rindex;
         void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
         Hashmap *names; /* For name compression */
+        size_t opt_start, opt_size;
 
         /* Parsed data */
         DnsQuestion *question;
@@ -118,7 +119,17 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
 #define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1)
 #define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
 #define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
-#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15)
+
+static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
+        uint16_t rcode;
+
+        if (p->opt)
+                rcode = (uint16_t) (p->opt->ttl >> 24);
+        else
+                rcode = 0;
+
+        return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15);
+}
 
 /* LLMNR defines some bits differently */
 #define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p)
@@ -173,9 +184,10 @@ int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonica
 int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start);
 int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start);
 int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start);
-int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
+int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
 
 void dns_packet_truncate(DnsPacket *p, size_t sz);
+int dns_packet_truncate_opt(DnsPacket *p);
 
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start);
 int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start);
@@ -201,6 +213,7 @@ static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) {
         return in_addr_is_localhost(p->family, &p->sender) == 0;
 }
 
+/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
 enum {
         DNS_RCODE_SUCCESS = 0,
         DNS_RCODE_FORMERR = 1,
@@ -234,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;
index 18d2d01bf28143edd94caebf2fdbfc485fbc377a..fc5bf4020f6b6c98fe67877d70d9ac159442f66c 100644 (file)
@@ -24,6 +24,7 @@
 #include "hostname-util.h"
 #include "local-addresses.h"
 #include "resolved-dns-query.h"
+#include "string-util.h"
 
 /* How long to wait for the query in total */
 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
@@ -217,6 +218,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 +226,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 +309,25 @@ 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_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 +341,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 +363,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 +415,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;
 
-        for (i = 0; i < question->n_keys; i++) {
-                _cleanup_free_ char *p;
+                r = dns_resource_key_to_string(key, &p);
+                if (r < 0)
+                        return r;
+
+                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 +524,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) {
@@ -534,7 +612,7 @@ static int dns_type_to_af(uint16_t t) {
         }
 }
 
-static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_localhost_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
         int r;
 
         assert(q);
@@ -590,7 +668,7 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to,
         return dns_answer_add(*answer, rr, ifindex, flags);
 }
 
-static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_localhost_ptr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
         int r;
 
         assert(q);
@@ -682,7 +760,7 @@ static int answer_add_addresses_ptr(
         return 0;
 }
 
-static int synthesize_system_hostname_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
+static int synthesize_system_hostname_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
         _cleanup_free_ struct local_address *addresses = NULL;
         int n = 0, af;
 
@@ -766,7 +844,7 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad
         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) {
+static int synthesize_gateway_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
         _cleanup_free_ struct local_address *addresses = NULL;
         int n = 0, af;
 
@@ -801,7 +879,7 @@ static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union
 
 static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned i;
+        DnsResourceKey *key;
         int r;
 
         assert(q);
@@ -816,39 +894,39 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
                     DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
                 return 0;
 
-        for (i = 0; i < q->question->n_keys; i++) {
+        DNS_QUESTION_FOREACH(key, q->question_utf8) {
                 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)
+                if (key->class != DNS_CLASS_IN &&
+                    key->class != DNS_CLASS_ANY)
                         continue;
 
-                name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
+                name = DNS_RESOURCE_KEY_NAME(key);
 
                 if (is_localhost(name)) {
 
-                        r = synthesize_localhost_rr(q, q->question->keys[i], &answer);
+                        r = synthesize_localhost_rr(q, key, &answer);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
 
                 } else if (manager_is_own_hostname(q->manager, name)) {
 
-                        r = synthesize_system_hostname_rr(q, q->question->keys[i], &answer);
+                        r = synthesize_system_hostname_rr(q, key, &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(q, q->question->keys[i], &answer);
+                        r = synthesize_gateway_rr(q, key, &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(q, q->question->keys[i], &answer);
+                        r = synthesize_localhost_ptr(q, key, &answer);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
 
@@ -884,7 +962,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 +969,13 @@ 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);
-
         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)
@@ -934,6 +1011,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)
@@ -961,6 +1043,8 @@ int dns_query_go(DnsQuery *q) {
         if (r < 0)
                 goto fail;
 
+        (void) sd_event_source_set_description(q->timeout_event_source, "query-timeout");
+
         q->state = DNS_TRANSACTION_PENDING;
         q->block_ready++;
 
@@ -1039,8 +1123,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
                         if (state == DNS_TRANSACTION_SUCCESS)
                                 continue;
 
-                        dns_answer_unref(q->answer);
-                        q->answer = dns_answer_ref(t->answer);
+                        q->answer = dns_answer_unref(q->answer);
                         q->answer_rcode = t->answer_rcode;
                         q->answer_dnssec_result = t->answer_dnssec_result;
 
@@ -1114,50 +1197,78 @@ 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;
 
         assert(q);
 
-        if (q->state != DNS_TRANSACTION_SUCCESS)
-                return 0;
+        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 0; /* The answer matches directly, no need to follow cnames */
+                        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)
@@ -1165,7 +1276,7 @@ int dns_query_process_cname(DnsQuery *q) {
         }
 
         if (!cname)
-                return 0; /* No cname to follow */
+                return DNS_QUERY_NOMATCH; /* No match and no cname to follow */
 
         if (q->flags & SD_RESOLVED_NO_CNAME)
                 return -ELOOP;
@@ -1177,20 +1288,16 @@ int dns_query_process_cname(DnsQuery *q) {
 
         /* Let's see if the answer can already answer the new
          * redirected question */
-        DNS_ANSWER_FOREACH(rr, q->answer) {
-                r = dns_question_matches_rr(q->question, rr, NULL);
-                if (r < 0)
-                        return r;
-                if (r > 0)
-                        return 0; /* It can answer it, yay! */
-        }
+        r = dns_query_process_cname(q);
+        if (r != DNS_QUERY_NOMATCH)
+                return r;
 
         /* OK, it cannot, let's begin with the new query */
         r = dns_query_go(q);
         if (r < 0)
                 return r;
 
-        return 1; /* We return > 0, if we restarted the query for a new cname */
+        return DNS_QUERY_RESTARTED; /* We restarted the query for a new cname */
 }
 
 static int on_bus_track(sd_bus_track *t, void *userdata) {
@@ -1222,3 +1329,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);
+}
index 44edd5bfff2d113a66252171db90e132ade26970..9f618d6f6bbe08705ef8f54258e30cb3d3cc0f9c 100644 (file)
@@ -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;
 
@@ -84,6 +90,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);
@@ -95,10 +102,16 @@ struct DnsQuery {
         LIST_FIELDS(DnsQuery, auxiliary_queries);
 };
 
+enum {
+        DNS_QUERY_MATCH,
+        DNS_QUERY_NOMATCH,
+        DNS_QUERY_RESTARTED,
+};
+
 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);
@@ -110,4 +123,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);
index 4ed7434d3c50865edd133fe15cdb2b871b571894..1e41a9aa3c9947d09c068db72b3c8b1f7ce001be 100644 (file)
@@ -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)
index 5ffb63e2504cd72718a1e65519535cbc8336532c..98e1f0e366dcdc7132efe09a612cb0467fb4fbfd 100644 (file)
@@ -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)                                \
index 98a3a3351d92a18727ec9e01a36b4d088e12f814..7273ef38255846dc47f6d2326ba567e930df93ee 100644 (file)
@@ -261,16 +261,13 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *
 
         /* Checks whether 'soa' is a SOA record for the specified key. */
 
-        if (soa->class != DNS_CLASS_IN)
+        if (soa->class != key->class)
                 return 0;
 
         if (soa->type != DNS_TYPE_SOA)
                 return 0;
 
-        if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
-                return 0;
-
-        return 1;
+        return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa));
 }
 
 static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
@@ -311,9 +308,12 @@ const struct hash_ops dns_resource_key_hash_ops = {
 
 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
         char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
-        const char *c, *t;
+        const char *c, *t, *n;
         char *s;
 
+        /* If we cannot convert the CLASS/TYPE into a known string,
+           use the format recommended by RFC 3597, Section 5. */
+
         c = dns_class_to_string(key->class);
         if (!c) {
                 sprintf(cbuf, "CLASS%u", key->class);
@@ -326,13 +326,54 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
                 t = tbuf;
         }
 
-        if (asprintf(&s, "%s. %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0)
+        n = DNS_RESOURCE_KEY_NAME(key);
+        if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0)
                 return -ENOMEM;
 
         *ret = s;
         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;
 
@@ -342,6 +383,8 @@ 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;
 }
@@ -451,6 +494,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
                 dns_resource_key_unref(rr->key);
         }
 
+        free(rr->to_string);
         free(rr);
 
         return NULL;
@@ -766,16 +810,19 @@ static char *format_txt(DnsTxtItem *first) {
         return s;
 }
 
-int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
+const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
         _cleanup_free_ char *k = NULL, *t = NULL;
         char *s;
         int r;
 
         assert(rr);
 
+        if (rr->to_string)
+                return rr->to_string;
+
         r = dns_resource_key_to_string(rr->key, &k);
         if (r < 0)
-                return r;
+                return NULL;
 
         switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
 
@@ -787,7 +834,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->srv.port,
                              strna(rr->srv.name));
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_PTR:
@@ -796,25 +843,25 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
         case DNS_TYPE_DNAME:
                 s = strjoin(k, " ", rr->ptr.name, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
 
                 break;
 
         case DNS_TYPE_HINFO:
                 s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_SPF: /* exactly the same as TXT */
         case DNS_TYPE_TXT:
                 t = format_txt(rr->txt.items);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 s = strjoin(k, " ", t, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_A: {
@@ -822,22 +869,22 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
 
                 r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
                 if (r < 0)
-                        return r;
+                        return NULL;
 
                 s = strjoin(k, " ", x, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
                 break;
         }
 
         case DNS_TYPE_AAAA:
                 r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
                 if (r < 0)
-                        return r;
+                        return NULL;
 
                 s = strjoin(k, " ", t, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_SOA:
@@ -851,7 +898,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->soa.expire,
                              rr->soa.minimum);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_MX:
@@ -860,7 +907,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->mx.priority,
                              rr->mx.exchange);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_LOC:
@@ -873,17 +920,17 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                                     rr->loc.horiz_pre,
                                     rr->loc.vert_pre);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 s = strjoin(k, " ", t, NULL);
                 if (!s)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_DS:
                 t = hexmem(rr->ds.digest, rr->ds.digest_size);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 r = asprintf(&s, "%s %u %u %u %s",
                              k,
@@ -892,13 +939,13 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->ds.digest_type,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_SSHFP:
                 t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 r = asprintf(&s, "%s %u %u %s",
                              k,
@@ -906,58 +953,62 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->sshfp.fptype,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_DNSKEY: {
-                const char *alg;
+                _cleanup_free_ char *alg = NULL;
 
-                alg = dnssec_algorithm_to_string(rr->dnskey.algorithm);
+                r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
+                if (r < 0)
+                        return NULL;
 
                 t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
-                r = asprintf(&s, "%s %u %u %.*s%.*u %s",
+                r = asprintf(&s, "%s %u %u %s %s",
                              k,
                              rr->dnskey.flags,
                              rr->dnskey.protocol,
-                             alg ? -1 : 0, alg,
-                             alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
+                             alg,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
         }
 
         case DNS_TYPE_RRSIG: {
-                const char *type, *alg;
+                _cleanup_free_ char *alg = NULL;
                 char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
+                const char *type;
 
                 type = dns_type_to_string(rr->rrsig.type_covered);
-                alg = dnssec_algorithm_to_string(rr->rrsig.algorithm);
+
+                r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg);
+                if (r < 0)
+                        return NULL;
 
                 t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
                 if (r < 0)
-                        return r;
+                        return NULL;
 
                 r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
                 if (r < 0)
-                        return r;
+                        return NULL;
 
                 /* TYPE?? follows
                  * http://tools.ietf.org/html/rfc3597#section-5 */
 
-                r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
+                r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %s",
                              k,
                              type ?: "TYPE",
                              type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
-                             alg ? -1 : 0, alg,
-                             alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm,
+                             alg,
                              rr->rrsig.labels,
                              rr->rrsig.original_ttl,
                              expiration,
@@ -966,21 +1017,21 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              rr->rrsig.signer,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
         }
 
         case DNS_TYPE_NSEC:
                 t = format_types(rr->nsec.types);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 r = asprintf(&s, "%s %s %s",
                              k,
                              rr->nsec.next_domain_name,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
 
         case DNS_TYPE_NSEC3: {
@@ -989,16 +1040,16 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                 if (rr->nsec3.salt_size > 0) {
                         salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
                         if (!salt)
-                                return -ENOMEM;
+                                return NULL;
                 }
 
                 hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
                 if (!hash)
-                        return -ENOMEM;
+                        return NULL;
 
                 t = format_types(rr->nsec3.types);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
                 r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
                              k,
@@ -1009,7 +1060,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              hash,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
 
                 break;
         }
@@ -1017,16 +1068,17 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
         default:
                 t = hexmem(rr->generic.data, rr->generic.size);
                 if (!t)
-                        return -ENOMEM;
+                        return NULL;
 
+                /* Format as documented in RFC 3597, Section 5 */
                 r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
                 break;
         }
 
-        *ret = s;
-        return 0;
+        rr->to_string = s;
+        return s;
 }
 
 int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
@@ -1074,6 +1126,239 @@ 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;
+
+        assert(rr);
+
+        dns_resource_key_hash_func(rr->key, state);
+
+        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+
+        case DNS_TYPE_SRV:
+                siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
+                siphash24_compress(&rr->srv.weight, sizeof(rr->srv.weight), state);
+                siphash24_compress(&rr->srv.port, sizeof(rr->srv.port), state);
+                dns_name_hash_func(rr->srv.name, state);
+                break;
+
+        case DNS_TYPE_PTR:
+        case DNS_TYPE_NS:
+        case DNS_TYPE_CNAME:
+        case DNS_TYPE_DNAME:
+                dns_name_hash_func(rr->ptr.name, state);
+                break;
+
+        case DNS_TYPE_HINFO:
+                string_hash_func(rr->hinfo.cpu, state);
+                string_hash_func(rr->hinfo.os, state);
+                break;
+
+        case DNS_TYPE_TXT:
+        case DNS_TYPE_SPF: {
+                DnsTxtItem *j;
+
+                LIST_FOREACH(items, j, rr->txt.items) {
+                        siphash24_compress(j->data, j->length, state);
+
+                        /* Add an extra NUL byte, so that "a" followed by "b" doesn't result in the same hash as "ab"
+                         * followed by "". */
+                        siphash24_compress_byte(0, state);
+                }
+                break;
+        }
+
+        case DNS_TYPE_A:
+                siphash24_compress(&rr->a.in_addr, sizeof(rr->a.in_addr), state);
+                break;
+
+        case DNS_TYPE_AAAA:
+                siphash24_compress(&rr->aaaa.in6_addr, sizeof(rr->aaaa.in6_addr), state);
+                break;
+
+        case DNS_TYPE_SOA:
+                dns_name_hash_func(rr->soa.mname, state);
+                dns_name_hash_func(rr->soa.rname, state);
+                siphash24_compress(&rr->soa.serial, sizeof(rr->soa.serial), state);
+                siphash24_compress(&rr->soa.refresh, sizeof(rr->soa.refresh), state);
+                siphash24_compress(&rr->soa.retry, sizeof(rr->soa.retry), state);
+                siphash24_compress(&rr->soa.expire, sizeof(rr->soa.expire), state);
+                siphash24_compress(&rr->soa.minimum, sizeof(rr->soa.minimum), state);
+                break;
+
+        case DNS_TYPE_MX:
+                siphash24_compress(&rr->mx.priority, sizeof(rr->mx.priority), state);
+                dns_name_hash_func(rr->mx.exchange, state);
+                break;
+
+        case DNS_TYPE_LOC:
+                siphash24_compress(&rr->loc.version, sizeof(rr->loc.version), state);
+                siphash24_compress(&rr->loc.size, sizeof(rr->loc.size), state);
+                siphash24_compress(&rr->loc.horiz_pre, sizeof(rr->loc.horiz_pre), state);
+                siphash24_compress(&rr->loc.vert_pre, sizeof(rr->loc.vert_pre), state);
+                siphash24_compress(&rr->loc.latitude, sizeof(rr->loc.latitude), state);
+                siphash24_compress(&rr->loc.longitude, sizeof(rr->loc.longitude), state);
+                siphash24_compress(&rr->loc.altitude, sizeof(rr->loc.altitude), state);
+                break;
+
+        case DNS_TYPE_SSHFP:
+                siphash24_compress(&rr->sshfp.algorithm, sizeof(rr->sshfp.algorithm), state);
+                siphash24_compress(&rr->sshfp.fptype, sizeof(rr->sshfp.fptype), state);
+                siphash24_compress(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, state);
+                break;
+
+        case DNS_TYPE_DNSKEY:
+                siphash24_compress(&rr->dnskey.flags, sizeof(rr->dnskey.flags), state);
+                siphash24_compress(&rr->dnskey.protocol, sizeof(rr->dnskey.protocol), state);
+                siphash24_compress(&rr->dnskey.algorithm, sizeof(rr->dnskey.algorithm), state);
+                siphash24_compress(rr->dnskey.key, rr->dnskey.key_size, state);
+                break;
+
+        case DNS_TYPE_RRSIG:
+                siphash24_compress(&rr->rrsig.type_covered, sizeof(rr->rrsig.type_covered), state);
+                siphash24_compress(&rr->rrsig.algorithm, sizeof(rr->rrsig.algorithm), state);
+                siphash24_compress(&rr->rrsig.labels, sizeof(rr->rrsig.labels), state);
+                siphash24_compress(&rr->rrsig.original_ttl, sizeof(rr->rrsig.original_ttl), state);
+                siphash24_compress(&rr->rrsig.expiration, sizeof(rr->rrsig.expiration), state);
+                siphash24_compress(&rr->rrsig.inception, sizeof(rr->rrsig.inception), state);
+                siphash24_compress(&rr->rrsig.key_tag, sizeof(rr->rrsig.key_tag), state);
+                dns_name_hash_func(rr->rrsig.signer, state);
+                siphash24_compress(rr->rrsig.signature, rr->rrsig.signature_size, state);
+                break;
+
+        case DNS_TYPE_NSEC:
+                dns_name_hash_func(rr->nsec.next_domain_name, state);
+                /* FIXME: we leave out the type bitmap here. Hash
+                 * would be better if we'd take it into account
+                 * too. */
+                break;
+
+        case DNS_TYPE_DS:
+                siphash24_compress(&rr->ds.key_tag, sizeof(rr->ds.key_tag), state);
+                siphash24_compress(&rr->ds.algorithm, sizeof(rr->ds.algorithm), state);
+                siphash24_compress(&rr->ds.digest_type, sizeof(rr->ds.digest_type), state);
+                siphash24_compress(rr->ds.digest, rr->ds.digest_size, state);
+                break;
+
+        case DNS_TYPE_NSEC3:
+                siphash24_compress(&rr->nsec3.algorithm, sizeof(rr->nsec3.algorithm), state);
+                siphash24_compress(&rr->nsec3.flags, sizeof(rr->nsec3.flags), state);
+                siphash24_compress(&rr->nsec3.iterations, sizeof(rr->nsec3.iterations), state);
+                siphash24_compress(rr->nsec3.salt, rr->nsec3.salt_size, state);
+                siphash24_compress(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, state);
+                /* FIXME: We leave the bitmaps out */
+                break;
+
+        default:
+                siphash24_compress(rr->generic.data, rr->generic.size, state);
+                break;
+        }
+}
+
+static int dns_resource_record_compare_func(const void *a, const void *b) {
+        const DnsResourceRecord *x = a, *y = b;
+        int ret;
+
+        ret = dns_resource_key_compare_func(x->key, y->key);
+        if (ret != 0)
+                return ret;
+
+        if (dns_resource_record_equal(x, y))
+                return 0;
+
+        /* This is a bit dirty, we don't implement proper odering, but
+         * the hashtable doesn't need ordering anyway, hence we don't
+         * care. */
+        return x < y ? -1 : 1;
+}
+
+const struct hash_ops dns_resource_record_hash_ops = {
+        .hash = dns_resource_record_hash_func,
+        .compare = dns_resource_record_compare_func,
+};
+
 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
         DnsTxtItem *n;
 
@@ -1107,6 +1392,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
 }
 
 static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
+        /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
         [DNSSEC_ALGORITHM_RSAMD5]             = "RSAMD5",
         [DNSSEC_ALGORITHM_DH]                 = "DH",
         [DNSSEC_ALGORITHM_DSA]                = "DSA",
@@ -1116,14 +1402,20 @@ static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] =
         [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
         [DNSSEC_ALGORITHM_RSASHA256]          = "RSASHA256",
         [DNSSEC_ALGORITHM_RSASHA512]          = "RSASHA512",
+        [DNSSEC_ALGORITHM_ECC_GOST]           = "ECC-GOST",
+        [DNSSEC_ALGORITHM_ECDSAP256SHA256]    = "ECDSAP256SHA256",
+        [DNSSEC_ALGORITHM_ECDSAP384SHA384]    = "ECDSAP384SHA384",
         [DNSSEC_ALGORITHM_INDIRECT]           = "INDIRECT",
         [DNSSEC_ALGORITHM_PRIVATEDNS]         = "PRIVATEDNS",
         [DNSSEC_ALGORITHM_PRIVATEOID]         = "PRIVATEOID",
 };
-DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255);
 
 static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
-        [DNSSEC_DIGEST_SHA1] = "SHA1",
-        [DNSSEC_DIGEST_SHA256] = "SHA256",
+        /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
+        [DNSSEC_DIGEST_SHA1] = "SHA-1",
+        [DNSSEC_DIGEST_SHA256] = "SHA-256",
+        [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
+        [DNSSEC_DIGEST_SHA384] = "SHA-384",
 };
-DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int);
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255);
index a35f01ce10ed28d190db455f9c263e4e9917978a..d9c31e81c5b47e989f86b60f3db3103768337853 100644 (file)
@@ -34,8 +34,9 @@ typedef struct DnsResourceRecord DnsResourceRecord;
 typedef struct DnsTxtItem DnsTxtItem;
 
 /* DNSKEY RR flags */
-#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
 #define DNSKEY_FLAG_SEP      (UINT16_C(1) << 0)
+#define DNSKEY_FLAG_REVOKE   (UINT16_C(1) << 7)
+#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8)
 
 /* mDNS RR flags */
 #define MDNS_RR_CACHE_FLUSH  (UINT16_C(1) << 15)
@@ -51,8 +52,11 @@ enum {
         DNSSEC_ALGORITHM_RSASHA1,
         DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
         DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
-        DNSSEC_ALGORITHM_RSASHA256 = 8,  /* RFC 5702 */
-        DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
+        DNSSEC_ALGORITHM_RSASHA256 = 8,        /* RFC 5702 */
+        DNSSEC_ALGORITHM_RSASHA512 = 10,       /* RFC 5702 */
+        DNSSEC_ALGORITHM_ECC_GOST = 12,        /* RFC 5933 */
+        DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */
+        DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
         DNSSEC_ALGORITHM_INDIRECT = 252,
         DNSSEC_ALGORITHM_PRIVATEDNS,
         DNSSEC_ALGORITHM_PRIVATEOID,
@@ -63,12 +67,21 @@ enum {
  * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
 enum {
         DNSSEC_DIGEST_SHA1 = 1,
-        DNSSEC_DIGEST_SHA256 = 2,
+        DNSSEC_DIGEST_SHA256 = 2,              /* RFC 4509 */
+        DNSSEC_DIGEST_GOST_R_34_11_94 = 3,     /* RFC 5933 */
+        DNSSEC_DIGEST_SHA384 = 4,              /* RFC 6605 */
         _DNSSEC_DIGEST_MAX_DEFINED
 };
 
+/* DNSSEC NSEC3 hash algorithms, see
+ * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
+enum {
+        NSEC3_ALGORITHM_SHA1 = 1,
+        _NSEC3_ALGORITHM_MAX_DEFINED
+};
+
 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()! */
 };
@@ -95,12 +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;
@@ -150,6 +175,7 @@ struct DnsResourceRecord {
                         char *exchange;
                 } mx;
 
+                /* https://tools.ietf.org/html/rfc1876 */
                 struct {
                         uint8_t version;
                         uint8_t size;
@@ -160,14 +186,6 @@ struct DnsResourceRecord {
                         uint32_t altitude;
                 } loc;
 
-                struct {
-                        uint16_t key_tag;
-                        uint8_t algorithm;
-                        uint8_t digest_type;
-                        void *digest;
-                        size_t digest_size;
-                } ds;
-
                 /* https://tools.ietf.org/html/rfc4255#section-3.1 */
                 struct {
                         uint8_t algorithm;
@@ -205,6 +223,15 @@ struct DnsResourceRecord {
                         Bitmap *types;
                 } nsec;
 
+                /* https://tools.ietf.org/html/rfc4034#section-5.1 */
+                struct {
+                        uint16_t key_tag;
+                        uint8_t algorithm;
+                        uint8_t digest_type;
+                        void *digest;
+                        size_t digest_size;
+                } ds;
+
                 struct {
                         uint8_t algorithm;
                         uint8_t flags;
@@ -219,7 +246,7 @@ struct DnsResourceRecord {
 };
 
 static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
-        if (_unlikely_(!key))
+        if (!key)
                 return NULL;
 
         if (key->_name)
@@ -228,6 +255,27 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
         return (char*) key + sizeof(DnsResourceKey);
 }
 
+static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) {
+        if (!rr)
+                return NULL;
+
+        if (!rr->wire_format)
+                return NULL;
+
+        assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
+        return (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset;
+}
+
+static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) {
+        if (!rr)
+                return 0;
+        if (!rr->wire_format)
+                return 0;
+
+        assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
+        return rr->wire_format_size - rr->wire_format_rdata_offset;
+}
+
 DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
 DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
 int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
@@ -246,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);
@@ -253,18 +303,24 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr);
 int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
 int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name);
 int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b);
-int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret);
+const char* dns_resource_record_to_string(DnsResourceRecord *rr);
 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);
 
 extern const struct hash_ops dns_resource_key_hash_ops;
+extern const struct hash_ops dns_resource_record_hash_ops;
 
-const char* dnssec_algorithm_to_string(int i) _const_;
+int dnssec_algorithm_to_string_alloc(int i, char **ret);
 int dnssec_algorithm_from_string(const char *s) _pure_;
 
-const char *dnssec_digest_to_string(int i) _const_;
+int dnssec_digest_to_string_alloc(int i, char **ret);
 int dnssec_digest_from_string(const char *s) _pure_;
index b284cb8b270067a95acee94614cd80412b672e2b..8a52d66fada235d93d18ddf7ce8418b29a67b4c8 100644 (file)
@@ -57,6 +57,21 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int
         s->family = family;
         s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
 
+        s->dnssec_mode = _DNSSEC_MODE_INVALID;
+
+        if (protocol == DNS_PROTOCOL_DNS) {
+                /* Copy DNSSEC mode from the link if it is set there,
+                 * otherwise take the manager's DNSSEC mode. Note that
+                 * we copy this only at scope creation time, and do
+                 * not update it from the on, even if the setting
+                 * changes. */
+
+                if (l)
+                        s->dnssec_mode = link_get_dnssec_mode(l);
+                else
+                        s->dnssec_mode = manager_get_dnssec_mode(m);
+        }
+
         LIST_PREPEND(scopes, m->dns_scopes, s);
 
         dns_scope_llmnr_membership(s, true);
@@ -162,17 +177,15 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
                 s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
 }
 
-static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
+static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
         union in_addr_union addr;
         int ifindex = 0, r;
         int family;
         uint32_t mtu;
-        size_t saved_size = 0;
 
         assert(s);
         assert(p);
         assert(p->protocol == s->protocol);
-        assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
 
         if (s->link) {
                 mtu = s->link->mtu;
@@ -181,30 +194,13 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
                 mtu = manager_find_mtu(s->manager);
 
         switch (s->protocol) {
+
         case DNS_PROTOCOL_DNS:
-                assert(server);
+                assert(fd >= 0);
 
                 if (DNS_PACKET_QDCOUNT(p) > 1)
                         return -EOPNOTSUPP;
 
-                if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
-                        bool edns_do;
-                        size_t packet_size;
-
-                        edns_do = server->possible_features >= DNS_SERVER_FEATURE_LEVEL_DO;
-
-                        if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_LARGE)
-                                packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
-                        else
-                                packet_size = server->received_udp_packet_max;
-
-                        r = dns_packet_append_opt_rr(p, packet_size, edns_do, &saved_size);
-                        if (r < 0)
-                                return r;
-
-                        DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) + 1);
-                }
-
                 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
                         return -EMSGSIZE;
 
@@ -215,15 +211,11 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
                 if (r < 0)
                         return r;
 
-                if (saved_size > 0) {
-                        dns_packet_truncate(p, saved_size);
-
-                        DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) - 1);
-                }
-
                 break;
 
         case DNS_PROTOCOL_LLMNR:
+                assert(fd < 0);
+
                 if (DNS_PACKET_QDCOUNT(p) > 1)
                         return -EOPNOTSUPP;
 
@@ -250,6 +242,8 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
                 break;
 
         case DNS_PROTOCOL_MDNS:
+                assert(fd < 0);
+
                 if (!ratelimit_test(&s->ratelimit))
                         return -EBUSY;
 
@@ -279,13 +273,13 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
         return 1;
 }
 
-int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
+int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) {
         int r;
 
         assert(s);
         assert(p);
         assert(p->protocol == s->protocol);
-        assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
+        assert((s->protocol == DNS_PROTOCOL_DNS) == (fd >= 0));
 
         do {
                 /* If there are multiple linked packets, set the TC bit in all but the last of them */
@@ -294,18 +288,24 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                         dns_packet_set_flags(p, true, true);
                 }
 
-                r = dns_scope_emit_one(s, fd, server, p);
+                r = dns_scope_emit_one(s, fd, p);
                 if (r < 0)
                         return r;
 
                 p = p->more;
-        } while(p);
+        } while (p);
 
         return 0;
 }
 
-static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
-        DnsServer *srv = NULL;
+static int dns_scope_socket(
+                DnsScope *s,
+                int type,
+                int family,
+                const union in_addr_union *address,
+                DnsServer *server,
+                uint16_t port) {
+
         _cleanup_close_ int fd = -1;
         union sockaddr_union sa = {};
         socklen_t salen;
@@ -313,31 +313,27 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
         int ret, r;
 
         assert(s);
-        assert((family == AF_UNSPEC) == !address);
 
-        if (family == AF_UNSPEC) {
-                srv = dns_scope_get_dns_server(s);
-                if (!srv)
-                        return -ESRCH;
+        if (server) {
+                assert(family == AF_UNSPEC);
+                assert(!address);
 
-                srv->possible_features = dns_server_possible_features(srv);
-
-                if (type == SOCK_DGRAM && srv->possible_features < DNS_SERVER_FEATURE_LEVEL_UDP)
-                        return -EAGAIN;
-
-                sa.sa.sa_family = srv->family;
-                if (srv->family == AF_INET) {
+                sa.sa.sa_family = server->family;
+                if (server->family == AF_INET) {
                         sa.in.sin_port = htobe16(port);
-                        sa.in.sin_addr = srv->address.in;
+                        sa.in.sin_addr = server->address.in;
                         salen = sizeof(sa.in);
-                } else if (srv->family == AF_INET6) {
+                } else if (server->family == AF_INET6) {
                         sa.in6.sin6_port = htobe16(port);
-                        sa.in6.sin6_addr = srv->address.in6;
+                        sa.in6.sin6_addr = server->address.in6;
                         sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
                         salen = sizeof(sa.in6);
                 } else
                         return -EAFNOSUPPORT;
         } else {
+                assert(family != AF_UNSPEC);
+                assert(address);
+
                 sa.sa.sa_family = family;
 
                 if (family == AF_INET) {
@@ -395,21 +391,18 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
         if (r < 0 && errno != EINPROGRESS)
                 return -errno;
 
-        if (server)
-                *server = srv;
-
         ret = fd;
         fd = -1;
 
         return ret;
 }
 
-int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
-        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
+        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
 }
 
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
-        return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
+        return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
 }
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
@@ -868,7 +861,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
                         return 0;
                 }
 
-                r = dns_scope_emit(scope, -1, NULL, p);
+                r = dns_scope_emit_udp(scope, -1, p);
                 if (r < 0)
                         log_debug_errno(r, "Failed to send conflict packet: %m");
         }
@@ -917,6 +910,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
 
+        (void) sd_event_source_set_description(scope->conflict_event_source, "scope-conflict");
+
         return 0;
 }
 
index 2fc2e07debbda89479599ccbaf4da45acf9601c5..a0676bd30ef0a4c636009da8455970e608bb36a4 100644 (file)
@@ -82,9 +82,9 @@ DnsScope* dns_scope_free(DnsScope *s);
 void dns_scope_packet_received(DnsScope *s, usec_t rtt);
 void dns_scope_packet_lost(DnsScope *s, usec_t usec);
 
-int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p);
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
-int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
+int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
index d565f99c09fe4b0cf82af173c65c41843885a0fc..5a866618075f8ebc901cdcefccc9c7aa24b868a0 100644 (file)
@@ -68,8 +68,8 @@ int dns_server_new(
 
         s->n_ref = 1;
         s->manager = m;
-        s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
-        s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
+        s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
+        s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
         s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
         s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
         s->type = type;
@@ -135,6 +135,7 @@ DnsServer* dns_server_unref(DnsServer *s)  {
         if (s->n_ref > 0)
                 return NULL;
 
+        free(s->server_string);
         free(s);
         return NULL;
 }
@@ -224,43 +225,75 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
         }
 }
 
-void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) {
+static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
         assert(s);
 
-        if (features == 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. */
-                if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
-                        s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
-                        assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
-                }
-        } else if (s->verified_features < features) {
-                s->verified_features = features;
-                assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
+        if (s->verified_feature_level > level)
+                return;
+
+        if (s->verified_feature_level != 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;
         }
 
-        if (s->possible_features == features)
-                s->n_failed_attempts = 0;
+        assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
+}
+
+void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
+        assert(s);
+
+        if (protocol == IPPROTO_UDP) {
+                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)
+                        level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
+
+        } else if (protocol == IPPROTO_TCP) {
+
+                if (s->possible_feature_level == level)
+                        s->n_failed_tcp = 0;
+
+                /* Successful TCP connections are only useful to verify the TCP feature level. */
+                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. */
-        if (s->received_udp_packet_max < size)
+        if (protocol == IPPROTO_UDP && s->received_udp_packet_max < size)
                 s->received_udp_packet_max = size;
 
         if (s->max_rtt < rtt) {
                 s->max_rtt = rtt;
-                s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
+                s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC);
         }
 }
 
-void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
+void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) {
         assert(s);
         assert(s->manager);
 
-        if (s->possible_features == features)
-                s->n_failed_attempts ++;
+        if (s->possible_feature_level == level) {
+                if (protocol == IPPROTO_UDP)
+                        s->n_failed_udp ++;
+                else if (protocol == IPPROTO_TCP)
+                        s->n_failed_tcp ++;
+        }
 
         if (s->resend_timeout > usec)
                 return;
@@ -268,14 +301,52 @@ void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t
         s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
 }
 
-void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features) {
+void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
         assert(s);
-        assert(s->manager);
 
-        if (s->possible_features != features)
+        /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */
+
+        if (s->possible_feature_level != level)
+                return;
+
+        s->packet_failed = true;
+}
+
+void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
+        assert(s);
+
+        /* Invoked whenever we get a packet with TC bit set. */
+
+        if (s->possible_feature_level != level)
                 return;
 
-        s->n_failed_attempts  = (unsigned) -1;
+        s->packet_truncated = true;
+}
+
+void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level) {
+        assert(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->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) {
@@ -297,35 +368,183 @@ static bool dns_server_grace_period_expired(DnsServer *s) {
         return true;
 }
 
-DnsServerFeatureLevel dns_server_possible_features(DnsServer *s) {
+static void dns_server_reset_counters(DnsServer *s) {
+        assert(s);
+
+        s->n_failed_udp = 0;
+        s->n_failed_tcp = 0;
+        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) {
         assert(s);
 
-        if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
+        if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
             dns_server_grace_period_expired(s)) {
-                _cleanup_free_ char *ip = NULL;
-
-                s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
-                s->n_failed_attempts = 0;
-                s->verified_usec = 0;
-
-                in_addr_to_string(s->family, &s->address, &ip);
-                log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
-        } else if (s->possible_features <= s->verified_features)
-                s->possible_features = s->verified_features;
-        else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
-                 s->possible_features > DNS_SERVER_FEATURE_LEVEL_WORST) {
-                _cleanup_free_ char *ip = NULL;
-
-                s->possible_features --;
-                s->n_failed_attempts = 0;
-                s->verified_usec = 0;
-
-                in_addr_to_string(s->family, &s->address, &ip);
-                log_warning("Using degraded feature set (%s) for DNS server %s",
-                            dns_server_feature_level_to_string(s->possible_features), strna(ip));
+
+                s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
+
+                dns_server_reset_counters(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));
+
+        } else if (s->possible_feature_level <= s->verified_feature_level)
+                s->possible_feature_level = s->verified_feature_level;
+        else {
+                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) {
+
+                        /* 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->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.",
+                                    dns_server_feature_level_to_string(s->possible_feature_level),
+                                    dns_server_string(s));
+                }
         }
 
-        return s->possible_features;
+        return s->possible_feature_level;
+}
+
+int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
+        size_t packet_size;
+        bool edns_do;
+        int r;
+
+        assert(server);
+        assert(packet);
+        assert(packet->protocol == DNS_PROTOCOL_DNS);
+
+        /* Fix the OPT field in the packet to match our current feature level. */
+
+        r = dns_packet_truncate_opt(packet);
+        if (r < 0)
+                return r;
+
+        if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
+                return 0;
+
+        edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
+
+        if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
+                packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
+        else
+                packet_size = server->received_udp_packet_max;
+
+        return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
+}
+
+const char *dns_server_string(DnsServer *server) {
+        assert(server);
+
+        if (!server->server_string)
+                (void) in_addr_to_string(server->family, &server->address, &server->server_string);
+
+        return strna(server->server_string);
+}
+
+bool dns_server_dnssec_supported(DnsServer *server) {
+        assert(server);
+
+        /* Returns whether the server supports DNSSEC according to what we know about it */
+
+        if (server->possible_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
+                return false;
+
+        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 */
+        if (server->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS)
+                return false;
+
+        return true;
 }
 
 static void dns_server_hash_func(const void *p, struct siphash *state) {
@@ -419,12 +638,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
         if (m->current_dns_server == s)
                 return s;
 
-        if (s) {
-                _cleanup_free_ char *ip = NULL;
-
-                in_addr_to_string(s->family, &s->address, &ip);
-                log_info("Switching to system DNS server %s.", strna(ip));
-        }
+        if (s)
+                log_info("Switching to system DNS server %s.", dns_server_string(s));
 
         dns_server_unref(m->current_dns_server);
         m->current_dns_server = dns_server_ref(s);
index b07fc3af3d00945416b2ae04f259eec57d3ab305..02bd3463a708e63085efe3618c786bb1e1e40317 100644 (file)
@@ -61,18 +61,30 @@ struct DnsServer {
         int family;
         union in_addr_union address;
 
-        bool marked:1;
+        char *server_string;
 
         usec_t resend_timeout;
         usec_t max_rtt;
 
-        DnsServerFeatureLevel verified_features;
-        DnsServerFeatureLevel possible_features;
+        DnsServerFeatureLevel verified_feature_level;
+        DnsServerFeatureLevel possible_feature_level;
+
         size_t received_udp_packet_max;
-        unsigned n_failed_attempts;
+
+        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;
 
+        /* Used when GC'ing old DNS servers when configuration changes. */
+        bool marked:1;
+
         /* If linked is set, then this server appears in the servers linked list */
         bool linked:1;
         LIST_FIELDS(DnsServer, servers);
@@ -92,9 +104,20 @@ DnsServer* dns_server_unref(DnsServer *s);
 void dns_server_unlink(DnsServer *s);
 void dns_server_move_back_and_unmark(DnsServer *s);
 
-void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size);
-void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec);
-void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features);
+void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size);
+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, DnsServerFeatureLevel level);
+void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level);
+
+DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
+
+int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
+
+const char *dns_server_string(DnsServer *server);
+
+bool dns_server_dnssec_supported(DnsServer *server);
 
 DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
 
@@ -110,6 +133,4 @@ void manager_next_dns_server(Manager *m);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
 
-DnsServerFeatureLevel dns_server_possible_features(DnsServer *s);
-
 extern const struct hash_ops dns_server_hash_ops;
index 1c501182fb77e5bba2321d23295e12a5093d6afb..b72e6cc06fb4585dacace3c704dc941ca3200b0d 100644 (file)
@@ -347,7 +347,6 @@ DnsStream *dns_stream_free(DnsStream *s) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free);
 
 int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
-        static const int one = 1;
         _cleanup_(dns_stream_freep) DnsStream *s = NULL;
         int r;
 
@@ -364,14 +363,12 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
         s->fd = -1;
         s->protocol = protocol;
 
-        r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
-        if (r < 0)
-                return -errno;
-
         r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
         if (r < 0)
                 return r;
 
+        (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
+
         r = sd_event_add_time(
                         m->event,
                         &s->timeout_event_source,
@@ -381,6 +378,8 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
         if (r < 0)
                 return r;
 
+        (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
+
         LIST_PREPEND(streams, m->dns_streams, s);
         s->manager = m;
         s->fd = fd;
index 347b1683f9a8b9356ab203a99187ecd69f89e7bf..5640cd1d3304fe9183c97e631aaa0f092f456477 100644 (file)
@@ -19,6 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sd-messages.h>
+
 #include "af-list.h"
 #include "alloc-util.h"
 #include "dns-domain.h"
 #include "resolved-llmnr.h"
 #include "string-table.h"
 
+#define TRANSACTIONS_MAX 4096
+
+static void dns_transaction_reset_answer(DnsTransaction *t) {
+        assert(t);
+
+        t->received = dns_packet_unref(t->received);
+        t->answer = dns_answer_unref(t->answer);
+        t->answer_rcode = 0;
+        t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+        t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
+        t->answer_authenticated = false;
+        t->answer_nsec_ttl = (uint32_t) -1;
+}
+
+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) {
+        assert(t);
+
+        t->stream = dns_stream_free(t->stream);
+        t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
+        t->dns_udp_fd = safe_close(t->dns_udp_fd);
+}
+
+static void dns_transaction_stop_timeout(DnsTransaction *t) {
+        assert(t);
+
+        t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
+}
+
 DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         DnsQueryCandidate *c;
         DnsZoneItem *i;
@@ -37,18 +78,15 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
         if (!t)
                 return NULL;
 
-        sd_event_source_unref(t->timeout_event_source);
-
-        dns_packet_unref(t->sent);
-        dns_packet_unref(t->received);
+        log_debug("Freeing transaction %" PRIu16 ".", t->id);
 
-        dns_answer_unref(t->answer);
+        dns_transaction_close_connection(t);
+        dns_transaction_stop_timeout(t);
 
-        sd_event_source_unref(t->dns_udp_event_source);
-        safe_close(t->dns_udp_fd);
+        dns_packet_unref(t->sent);
+        dns_transaction_reset_answer(t);
 
         dns_server_unref(t->server);
-        dns_stream_free(t->stream);
 
         if (t->scope) {
                 hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
@@ -58,8 +96,6 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
                         hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
         }
 
-        dns_resource_key_unref(t->key);
-
         while ((c = set_steal_first(t->notify_query_candidates)))
                 set_remove(c->transactions, t);
         set_free(t->notify_query_candidates);
@@ -72,31 +108,49 @@ 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);
-
+        dns_resource_key_unref(t->key);
         free(t->key_string);
+
         free(t);
         return NULL;
 }
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
 
-void dns_transaction_gc(DnsTransaction *t) {
+bool dns_transaction_gc(DnsTransaction *t) {
         assert(t);
 
         if (t->block_gc > 0)
-                return;
+                return true;
 
         if (set_isempty(t->notify_query_candidates) &&
             set_isempty(t->notify_zone_items) &&
-            set_isempty(t->notify_transactions))
+            set_isempty(t->notify_transactions)) {
                 dns_transaction_free(t);
+                return false;
+        }
+
+        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) {
@@ -110,11 +164,16 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         /* Don't allow looking up invalid or pseudo RRs */
         if (!dns_type_is_valid_query(key->type))
                 return -EINVAL;
+        if (dns_type_is_obsolete(key->type))
+                return -EOPNOTSUPP;
 
         /* We only support the IN class */
         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;
@@ -130,13 +189,11 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         t->dns_udp_fd = -1;
         t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
         t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+        t->answer_nsec_ttl = (uint32_t) -1;
         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) {
@@ -153,6 +210,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         LIST_PREPEND(transactions_by_scope, s->transactions, t);
         t->scope = s;
 
+        s->manager->n_transactions_total ++;
+
         if (ret)
                 *ret = t;
 
@@ -161,14 +220,20 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         return 0;
 }
 
-static void dns_transaction_stop(DnsTransaction *t) {
+static void dns_transaction_shuffle_id(DnsTransaction *t) {
+        uint16_t new_id;
         assert(t);
 
-        t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
-        t->stream = dns_stream_free(t->stream);
+        /* 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);
 
-        /* Note that we do not drop the UDP socket here, as we want to
-         * reuse it to repeat the interaction. */
+        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) {
@@ -224,6 +289,15 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
         assert(t);
         assert(!DNS_TRANSACTION_IS_LIVE(state));
 
+        if (state == DNS_TRANSACTION_DNSSEC_FAILED)
+                log_struct(LOG_NOTICE,
+                           LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE),
+                           LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)),
+                           "DNS_TRANSACTION=%" PRIu16, t->id,
+                           "DNS_QUESTION=%s", dns_transaction_key_string(t),
+                           "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
+                           NULL);
+
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
          * after calling this function. */
@@ -240,7 +314,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
 
         t->state = state;
 
-        dns_transaction_stop(t);
+        dns_transaction_close_connection(t);
+        dns_transaction_stop_timeout(t);
 
         /* Notify all queries that are interested, but make sure the
          * transaction isn't freed while we are still looking at it */
@@ -284,6 +359,62 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
         dns_transaction_gc(t);
 }
 
+static int dns_transaction_pick_server(DnsTransaction *t) {
+        DnsServer *server;
+
+        assert(t);
+        assert(t->scope->protocol == DNS_PROTOCOL_DNS);
+
+        server = dns_scope_get_dns_server(t->scope);
+        if (!server)
+                return -ESRCH;
+
+        t->current_feature_level = dns_server_possible_feature_level(server);
+
+        if (server == t->server)
+                return 0;
+
+        dns_server_unref(t->server);
+        t->server = dns_server_ref(server);
+
+        return 1;
+}
+
+static void dns_transaction_retry(DnsTransaction *t) {
+        int r;
+
+        assert(t);
+
+        log_debug("Retrying transaction %" PRIu16 ".", t->id);
+
+        /* Before we try again, switch to a new server. */
+        dns_scope_next_dns_server(t->scope);
+
+        r = dns_transaction_go(t);
+        if (r < 0)
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+}
+
+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) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         DnsTransaction *t;
@@ -298,6 +429,16 @@ static int on_stream_complete(DnsStream *s, int error) {
 
         t->stream = dns_stream_free(t->stream);
 
+        if (ERRNO_IS_DISCONNECT(error)) {
+                usec_t usec;
+
+                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);
+
+                dns_transaction_retry(t);
+                return 0;
+        }
         if (error != 0) {
                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                 return 0;
@@ -315,32 +456,46 @@ static int on_stream_complete(DnsStream *s, int error) {
         dns_transaction_process_reply(t, p);
         t->block_gc--;
 
-        /* If the response wasn't useful, then complete the transition now */
+        /* If the response wasn't useful, then complete the transition
+         * now. After all, we are the worst feature set now with TCP
+         * sockets, and there's really no point in retrying. */
         if (t->state == DNS_TRANSACTION_PENDING)
                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+        else
+                dns_transaction_gc(t);
 
         return 0;
 }
 
 static int dns_transaction_open_tcp(DnsTransaction *t) {
-        DnsServer *server = NULL;
         _cleanup_close_ int fd = -1;
         int r;
 
         assert(t);
 
-        if (t->stream)
-                return 0;
+        dns_transaction_close_connection(t);
 
         switch (t->scope->protocol) {
+
         case DNS_PROTOCOL_DNS:
-                fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
+                r = dns_transaction_pick_server(t);
+                if (r < 0)
+                        return r;
+
+                if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
+                        return -EOPNOTSUPP;
+
+                r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
+                if (r < 0)
+                        return r;
+
+                fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
                 break;
 
         case DNS_PROTOCOL_LLMNR:
                 /* When we already received a reply to this (but it was truncated), send to its sender address */
                 if (t->received)
-                        fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
+                        fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
                 else {
                         union in_addr_union address;
                         int family = AF_UNSPEC;
@@ -357,7 +512,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
                         if (family != t->scope->family)
                                 return -ESRCH;
 
-                        fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
+                        fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
                 }
 
                 break;
@@ -372,7 +527,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
         r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
         if (r < 0)
                 return r;
-
         fd = -1;
 
         r = dns_stream_write_packet(t->stream, t->sent);
@@ -381,11 +535,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
                 return r;
         }
 
-        dns_server_unref(t->server);
-        t->server = dns_server_ref(server);
-        t->received = dns_packet_unref(t->received);
-        t->answer = dns_answer_unref(t->answer);
-        t->answer_rcode = 0;
         t->stream->complete = on_stream_complete;
         t->stream->transaction = t;
 
@@ -395,17 +544,11 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
         if (t->scope->link)
                 t->stream->ifindex = t->scope->link->ifindex;
 
-        return 0;
-}
-
-static void dns_transaction_next_dns_server(DnsTransaction *t) {
-        assert(t);
+        dns_transaction_reset_answer(t);
 
-        t->server = dns_server_unref(t->server);
-        t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
-        t->dns_udp_fd = safe_close(t->dns_udp_fd);
+        t->tried_stream = true;
 
-        dns_scope_next_dns_server(t->scope);
+        return 0;
 }
 
 static void dns_transaction_cache_answer(DnsTransaction *t) {
@@ -428,6 +571,7 @@ static void dns_transaction_cache_answer(DnsTransaction *t) {
                       t->answer_rcode,
                       t->answer,
                       t->answer_authenticated,
+                      t->answer_nsec_ttl,
                       0,
                       t->received->family,
                       &t->received->sender);
@@ -446,13 +590,86 @@ 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) {
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                return;
+        }
+        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) {
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                return;
+        }
+        if (r > 0) /* Transaction got restarted... */
                 return;
 
         /* All our auxiliary DNSSEC transactions are complete now. Try
@@ -463,10 +680,19 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
                 return;
         }
 
+        if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
+            t->scope->dnssec_mode == DNSSEC_YES) {
+                /*  We are not in automatic downgrade mode, and the
+                 *  server is bad, refuse operation. */
+                dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+                return;
+        }
+
         if (!IN_SET(t->answer_dnssec_result,
-                    _DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */
-                    DNSSEC_VALIDATED,       /* Answer is signed and validated successfully */
-                    DNSSEC_UNSIGNED)) {     /* Answer is right-fully unsigned */
+                    _DNSSEC_RESULT_INVALID,        /* No DNSSEC validation enabled */
+                    DNSSEC_VALIDATED,              /* Answer is signed and validated successfully */
+                    DNSSEC_UNSIGNED,               /* Answer is right-fully unsigned */
+                    DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
                 dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
                 return;
         }
@@ -485,10 +711,12 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
 
         assert(t);
         assert(p);
-        assert(t->state == DNS_TRANSACTION_PENDING);
         assert(t->scope);
         assert(t->scope->manager);
 
+        if (t->state != DNS_TRANSACTION_PENDING)
+                return;
+
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
          * after calling this function. */
@@ -532,6 +760,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 break;
 
         case DNS_PROTOCOL_DNS:
+                /* Note that we do not need to verify the
+                 * addresses/port numbers of incoming traffic, as we
+                 * invoked connect() on our UDP socket in which case
+                 * the kernel already does the needed verification for
+                 * us. */
                 break;
 
         default:
@@ -571,17 +804,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                         /* Request failed, immediately try again with reduced features */
                         log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p)));
 
-                        dns_server_packet_failed(t->server, t->current_features);
-
-                        r = dns_transaction_go(t);
-                        if (r < 0) {
-                                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
-                                return;
-                        }
-
+                        dns_server_packet_failed(t->server, t->current_feature_level);
+                        dns_transaction_retry(t);
                         return;
-                } else
-                        dns_server_packet_received(t->server, t->current_features, ts - t->start_usec, p->size);
+                } else if (DNS_PACKET_TC(p))
+                        dns_server_packet_truncated(t->server, t->current_feature_level);
 
                 break;
 
@@ -602,6 +829,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                         return;
                 }
 
+                log_debug("Reply truncated, retrying via TCP.");
+
                 /* Response was truncated, let's try again with good old TCP */
                 r = dns_transaction_open_tcp(t);
                 if (r == -ESRCH) {
@@ -609,34 +838,50 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                         dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
                         return;
                 }
+                if (r == -EOPNOTSUPP) {
+                        /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
+                        dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+                        return;
+                }
                 if (r < 0) {
                         /* On LLMNR, if we cannot connect to the host,
                          * we immediately give up */
-                        if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+                        if (t->scope->protocol != DNS_PROTOCOL_DNS) {
                                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                                 return;
                         }
 
                         /* On DNS, couldn't send? Try immediately again, with a new server */
-                        dns_transaction_next_dns_server(t);
-
-                        r = dns_transaction_go(t);
-                        if (r < 0) {
-                                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
-                                return;
-                        }
-
-                        return;
+                        dns_transaction_retry(t);
                 }
+
+                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) {
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                return;
+        }
+        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 */
@@ -654,9 +899,25 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 dns_answer_unref(t->answer);
                 t->answer = dns_answer_ref(p->answer);
                 t->answer_rcode = DNS_PACKET_RCODE(p);
+                t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
                 t->answer_authenticated = false;
 
+                /* Block GC while starting requests for additional DNSSEC RRs */
+                t->block_gc++;
                 r = dns_transaction_request_dnssec_keys(t);
+                t->block_gc--;
+
+                /* Maybe the transaction is ready for GC'ing now? If so, free it and return. */
+                if (!dns_transaction_gc(t))
+                        return;
+
+                /* Requesting additional keys might have resulted in
+                 * this transaction to fail, since the auxiliary
+                 * request failed for some reason. If so, we are not
+                 * in pending state anymore, and we should exit
+                 * quickly. */
+                if (t->state != DNS_TRANSACTION_PENDING)
+                        return;
                 if (r < 0) {
                         dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                         return;
@@ -664,7 +925,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 if (r > 0) {
                         /* There are DNSSEC transactions pending now. Update the state accordingly. */
                         t->state = DNS_TRANSACTION_VALIDATING;
-                        dns_transaction_stop(t);
+                        dns_transaction_close_connection(t);
+                        dns_transaction_stop_timeout(t);
                         return;
                 }
         }
@@ -681,53 +943,96 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
         assert(t->scope);
 
         r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
-        if (r <= 0)
-                return r;
+        if (ERRNO_IS_DISCONNECT(-r)) {
+                usec_t usec;
 
-        if (dns_packet_validate_reply(p) > 0 &&
-            DNS_PACKET_ID(p) == t->id)
-                dns_transaction_process_reply(t, p);
-        else
-                log_debug("Invalid DNS packet, ignoring.");
+                /* UDP connection failure get reported via ICMP and then are possible delivered to us on the next
+                 * recvmsg(). Treat this like a lost packet. */
+
+                log_debug_errno(r, "Connection failure for DNS UDP packet: %m");
+                assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
+                dns_server_packet_lost(t->server, IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
+
+                dns_transaction_retry(t);
+                return 0;
+        }
+        if (r < 0) {
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                return 0;
+        }
 
+        r = dns_packet_validate_reply(p);
+        if (r < 0) {
+                log_debug_errno(r, "Received invalid DNS packet as response, ignoring: %m");
+                return 0;
+        }
+        if (r == 0) {
+                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.");
+                return 0;
+        }
+
+        dns_transaction_process_reply(t, p);
         return 0;
 }
 
-static int dns_transaction_emit(DnsTransaction *t) {
+static int dns_transaction_emit_udp(DnsTransaction *t) {
         int r;
 
         assert(t);
 
-        if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
-                DnsServer *server = NULL;
-                _cleanup_close_ int fd = -1;
-
-                fd = dns_scope_udp_dns_socket(t->scope, &server);
-                if (fd < 0)
-                        return fd;
+        if (t->scope->protocol == DNS_PROTOCOL_DNS) {
 
-                r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
+                r = dns_transaction_pick_server(t);
                 if (r < 0)
                         return r;
 
-                t->dns_udp_fd = fd;
-                fd = -1;
-                t->server = dns_server_ref(server);
-        }
+                if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP)
+                        return -EAGAIN;
+
+                if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type))
+                        return -EOPNOTSUPP;
+
+                if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */
+                        int fd;
+
+                        dns_transaction_close_connection(t);
 
-        r = dns_scope_emit(t->scope, t->dns_udp_fd, t->server, t->sent);
+                        fd = dns_scope_socket_udp(t->scope, t->server, 53);
+                        if (fd < 0)
+                                return fd;
+
+                        r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
+                        if (r < 0) {
+                                safe_close(fd);
+                                return r;
+                        }
+
+                        (void) sd_event_source_set_description(t->dns_udp_event_source, "dns-transaction-udp");
+                        t->dns_udp_fd = fd;
+                }
+
+                r = dns_server_adjust_opt(t->server, t->sent, t->current_feature_level);
+                if (r < 0)
+                        return r;
+        } else
+                dns_transaction_close_connection(t);
+
+        r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->sent);
         if (r < 0)
                 return r;
 
-        if (t->server)
-                t->current_features = t->server->possible_features;
+        dns_transaction_reset_answer(t);
 
         return 0;
 }
 
 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
         DnsTransaction *t = userdata;
-        int r;
 
         assert(s);
         assert(t);
@@ -735,17 +1040,17 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
         if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) {
                 /* Timeout reached? Increase the timeout for the server used */
                 switch (t->scope->protocol) {
+
                 case DNS_PROTOCOL_DNS:
                         assert(t->server);
-
-                        dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec);
-
+                        dns_server_packet_lost(t->server, t->stream ? IPPROTO_TCP : IPPROTO_UDP, t->current_feature_level, usec - t->start_usec);
                         break;
+
                 case DNS_PROTOCOL_LLMNR:
                 case DNS_PROTOCOL_MDNS:
                         dns_scope_packet_lost(t->scope, usec - t->start_usec);
-
                         break;
+
                 default:
                         assert_not_reached("Invalid DNS protocol.");
                 }
@@ -756,13 +1061,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
 
         log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
 
-        /* ...and try again with a new server */
-        dns_transaction_next_dns_server(t);
-
-        r = dns_transaction_go(t);
-        if (r < 0)
-                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
-
+        dns_transaction_retry(t);
         return 0;
 }
 
@@ -771,36 +1070,36 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
         assert(t->scope);
 
         switch (t->scope->protocol) {
+
         case DNS_PROTOCOL_DNS:
                 assert(t->server);
-
                 return t->server->resend_timeout;
+
         case DNS_PROTOCOL_MDNS:
                 assert(t->n_attempts > 0);
                 return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+
         case DNS_PROTOCOL_LLMNR:
                 return t->scope->resend_timeout;
+
         default:
                 assert_not_reached("Invalid DNS protocol.");
         }
 }
 
 static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
-        bool had_stream;
         int r;
 
         assert(t);
 
-        had_stream = !!t->stream;
-
-        dns_transaction_stop(t);
+        dns_transaction_stop_timeout(t);
 
         if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
                 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
                 return 0;
         }
 
-        if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
+        if (t->scope->protocol == DNS_PROTOCOL_LLMNR && t->tried_stream) {
                 /* If we already tried via a stream, then we don't
                  * retry on LLMNR. See RFC 4795, Section 2.7. */
                 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
@@ -809,14 +1108,13 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
 
         t->n_attempts++;
         t->start_usec = ts;
-        t->received = dns_packet_unref(t->received);
-        t->answer = dns_answer_unref(t->answer);
-        t->answer_rcode = 0;
-        t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
+
+        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) {
-                r = dns_trust_anchor_lookup(&t->scope->manager->trust_anchor, t->key, &t->answer);
+                r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, t->key, &t->answer);
                 if (r < 0)
                         return r;
                 if (r > 0) {
@@ -826,6 +1124,41 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
                         dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
                         return 0;
                 }
+
+                if (dns_name_is_root(DNS_RESOURCE_KEY_NAME(t->key)) &&
+                    t->key->type == DNS_TYPE_DS) {
+
+                        /* Hmm, this is a request for the root DS? A
+                         * DS RR doesn't exist in the root zone, and
+                         * if our trust anchor didn't know it either,
+                         * this means we cannot do any DNSSEC logic
+                         * anymore. */
+
+                        if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
+                                /* We are in downgrade mode. In this
+                                 * case, synthesize an unsigned empty
+                                 * response, so that the any lookup
+                                 * depending on this one can continue
+                                 * assuming there was no DS, and hence
+                                 * the root zone was unsigned. */
+
+                                t->answer_rcode = DNS_RCODE_SUCCESS;
+                                t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
+                                t->answer_authenticated = false;
+                                dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+                        } else
+                                /* If we are not in downgrade mode,
+                                 * then fail the lookup, because we
+                                 * cannot reasonably answer it. There
+                                 * might be DS RRs, but we don't know
+                                 * them, and the DNS server won't tell
+                                 * them to us (and even if it would,
+                                 * we couldn't validate it and trust
+                                 * it). */
+                                dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR);
+
+                        return 0;
+                }
         }
 
         /* Check the zone, but only if this transaction is not used
@@ -950,6 +1283,8 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
                 if (r < 0)
                         return r;
 
+                (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
                 other->state = DNS_TRANSACTION_PENDING;
                 other->next_attempt_after = ts;
 
@@ -986,7 +1321,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
         if (t->sent)
                 return 0;
 
-        r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES);
+        r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode != DNSSEC_NO);
         if (r < 0)
                 return r;
 
@@ -1041,10 +1376,12 @@ int dns_transaction_go(DnsTransaction *t) {
                 random_bytes(&jitter, sizeof(jitter));
 
                 switch (t->scope->protocol) {
+
                 case DNS_PROTOCOL_LLMNR:
                         jitter %= LLMNR_JITTER_INTERVAL_USEC;
                         accuracy = LLMNR_JITTER_INTERVAL_USEC;
                         break;
+
                 case DNS_PROTOCOL_MDNS:
                         jitter %= MDNS_JITTER_RANGE_USEC;
                         jitter += MDNS_JITTER_MIN_USEC;
@@ -1063,6 +1400,8 @@ int dns_transaction_go(DnsTransaction *t) {
                 if (r < 0)
                         return r;
 
+                (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
                 t->n_attempts = 0;
                 t->next_attempt_after = ts;
                 t->state = DNS_TRANSACTION_PENDING;
@@ -1093,7 +1432,11 @@ int dns_transaction_go(DnsTransaction *t) {
         } else {
                 /* Try via UDP, and if that fails due to large size or lack of
                  * support try via TCP */
-                r = dns_transaction_emit(t);
+                r = dns_transaction_emit_udp(t);
+                if (r == -EMSGSIZE)
+                        log_debug("Sending query via TCP since it is too large.");
+                if (r == -EAGAIN)
+                        log_debug("Sending query via TCP since server doesn't support UDP.");
                 if (r == -EMSGSIZE || r == -EAGAIN)
                         r = dns_transaction_open_tcp(t);
         }
@@ -1102,14 +1445,20 @@ int dns_transaction_go(DnsTransaction *t) {
                 /* No servers to send this to? */
                 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
                 return 0;
-        } else if (r < 0) {
+        }
+        if (r == -EOPNOTSUPP) {
+                /* Tried to ask for DNSSEC RRs, on a server that doesn't do DNSSEC  */
+                dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
+                return 0;
+        }
+        if (r < 0) {
                 if (t->scope->protocol != DNS_PROTOCOL_DNS) {
                         dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
                         return 0;
                 }
 
                 /* Couldn't send? Try immediately again, with a new server */
-                dns_transaction_next_dns_server(t);
+                dns_scope_next_dns_server(t->scope);
 
                 return dns_transaction_go(t);
         }
@@ -1125,12 +1474,36 @@ int dns_transaction_go(DnsTransaction *t) {
         if (r < 0)
                 return r;
 
+        (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
         t->state = DNS_TRANSACTION_PENDING;
         t->next_attempt_after = ts;
 
         return 1;
 }
 
+static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) {
+        DnsTransaction *n;
+        Iterator i;
+        int r;
+
+        assert(t);
+        assert(aux);
+
+        /* Try to find cyclic dependencies between transaction objects */
+
+        if (t == aux)
+                return 1;
+
+        SET_FOREACH(n, aux->dnssec_transactions, i) {
+                r = dns_transaction_find_cyclic(t, n);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
         DnsTransaction *aux;
         int r;
@@ -1149,6 +1522,18 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource
                         *ret = aux;
                         return 0;
                 }
+
+                r = dns_transaction_find_cyclic(t, aux);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        log_debug("Detected potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).",
+                                  aux->id,
+                                  strna(dns_transaction_key_string(aux)),
+                                  t->id,
+                                  strna(dns_transaction_key_string(t)));
+                        return -ELOOP;
+                }
         }
 
         r = set_ensure_allocated(&t->dnssec_transactions, NULL);
@@ -1185,14 +1570,8 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
         assert(t);
         assert(key);
 
-        r = dns_resource_key_equal(t->key, key);
-        if (r < 0)
-                return r;
-        if (r > 0) /* Don't go in circles */
-                return 0;
-
         /* Try to get the data from the trust anchor */
-        r = dns_trust_anchor_lookup(&t->scope->manager->trust_anchor, key, &a);
+        r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a);
         if (r < 0)
                 return r;
         if (r > 0) {
@@ -1205,6 +1584,8 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
 
         /* This didn't work, ask for it via the network/cache then. */
         r = dns_transaction_add_dnssec_transaction(t, key, &aux);
+        if (r == -ELOOP) /* This would result in a cyclic dependency */
+                return 0;
         if (r < 0)
                 return r;
 
@@ -1214,7 +1595,7 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
                         return r;
         }
 
-        return 0;
+        return 1;
 }
 
 static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
@@ -1236,6 +1617,25 @@ static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags
         return false;
 }
 
+static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
+        int r;
+
+        assert(t);
+
+        /* Check whether the specified name is in the the NTA
+         * database, either in the global one, or the link-local
+         * one. */
+
+        r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, name);
+        if (r != 0)
+                return r;
+
+        if (!t->scope->link)
+                return 0;
+
+        return set_contains(t->scope->link->dnssec_negative_trust_anchors, name);
+}
+
 static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
         int r;
 
@@ -1250,6 +1650,14 @@ static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
         if (r > 0)
                 return false;
 
+        /* Is this key explicitly listed as a negative trust anchor?
+         * If so, it's nothing we need to care about */
+        r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
         /* The answer does not contain any RRs that match to the
          * question. If so, let's see if there are any NSEC/NSEC3 RRs
          * included. If not, the answer is unsigned. */
@@ -1300,6 +1708,44 @@ static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRec
         return rr->key->type == DNS_TYPE_NSEC;
 }
 
+static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
+        assert(t);
+
+        /* Checks whether our transaction's DNS server is assumed to be compatible with DNSSEC. Returns false as soon
+         * as we changed our mind about a server, and now believe it is incompatible with DNSSEC. */
+
+        if (t->scope->protocol != DNS_PROTOCOL_DNS)
+                return false;
+
+        /* If we have picked no server, then we are working from the cache or some other source, and DNSSEC might well
+         * be supported, hence return true. */
+        if (!t->server)
+                return true;
+
+        if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_DO)
+                return false;
+
+        return dns_server_dnssec_supported(t->server);
+}
+
+static bool dns_transaction_dnssec_supported_full(DnsTransaction *t) {
+        DnsTransaction *dt;
+        Iterator i;
+
+        assert(t);
+
+        /* Checks whether our transaction our any of the auxiliary transactions couldn't do DNSSEC. */
+
+        if (!dns_transaction_dnssec_supported(t))
+                return false;
+
+        SET_FOREACH(dt, t->dnssec_transactions, i)
+                if (!dns_transaction_dnssec_supported(dt))
+                        return false;
+
+        return true;
+}
+
 int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
         DnsResourceRecord *rr;
 
@@ -1315,20 +1761,31 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
          * - For RRSIG we get the matching DNSKEY
          * - For DNSKEY we get the matching DS
          * - For unsigned SOA/NS we get the matching DS
-         * - For unsigned CNAME/DNAME we get the parent SOA RR
+         * - For unsigned CNAME/DNAME/DS we get the parent SOA RR
          * - For other unsigned RRs we get the matching SOA RR
          * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
          * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
          */
 
-        if (t->scope->dnssec_mode != DNSSEC_YES)
+        if (t->scope->dnssec_mode == DNSSEC_NO)
                 return 0;
+        if (t->answer_source != DNS_TRANSACTION_NETWORK)
+                return 0; /* We only need to validate stuff from the network */
+        if (!dns_transaction_dnssec_supported(t))
+                return 0; /* If we can't do DNSSEC anyway there's no point in geting the auxiliary RRs */
 
         DNS_ANSWER_FOREACH(rr, t->answer) {
 
                 if (dns_type_is_pseudo(rr->key->type))
                         continue;
 
+                /* If this RR is in the negative trust anchor, we don't need to validate it. */
+                r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        continue;
+
                 switch (rr->key->type) {
 
                 case DNS_TYPE_RRSIG: {
@@ -1396,7 +1853,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         if (!ds)
                                 return -ENOMEM;
 
-                        log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr));
+                        log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr, false));
                         r = dns_transaction_request_dnssec_rr(t, ds);
                         if (r < 0)
                                 return r;
@@ -1404,15 +1861,6 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         break;
                 }
 
-                case DNS_TYPE_DS:
-                case DNS_TYPE_NSEC:
-                case DNS_TYPE_NSEC3:
-                        /* Don't acquire anything for
-                         * DS/NSEC/NSEC3. We require they come with an
-                         * RRSIG without us asking for anything, and
-                         * that's sufficient. */
-                        break;
-
                 case DNS_TYPE_SOA:
                 case DNS_TYPE_NS: {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
@@ -1448,6 +1896,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         break;
                 }
 
+                case DNS_TYPE_DS:
                 case DNS_TYPE_CNAME:
                 case DNS_TYPE_DNAME: {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
@@ -1458,7 +1907,10 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                          * unsigned CNAME/DNAME RRs, maybe that's the
                          * apex. But do all that only if this is
                          * actually a response to our original
-                         * question. */
+                         * question.
+                         *
+                         * Similar for DS RRs, which are signed when
+                         * the parent SOA is signed. */
 
                         r = dns_transaction_is_primary_response(t, rr);
                         if (r < 0)
@@ -1472,6 +1924,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)
@@ -1483,7 +1941,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         if (!soa)
                                 return -ENOMEM;
 
-                        log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
+                        log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
                         r = dns_transaction_request_dnssec_rr(t, soa);
                         if (r < 0)
                                 return r;
@@ -1494,10 +1952,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                 default: {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
 
-                        /* For other unsigned RRsets, look for proof
-                         * the zone is unsigned, by requesting the SOA
-                         * RR of the zone. However, do so only if they
-                         * are directly relevant to our original
+                        /* For other unsigned RRsets (including
+                         * NSEC/NSEC3!), look for proof the zone is
+                         * unsigned, by requesting the SOA RR of the
+                         * zone. However, do so only if they are
+                         * directly relevant to our original
                          * question. */
 
                         r = dns_transaction_is_primary_response(t, rr);
@@ -1516,7 +1975,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         if (!soa)
                                 return -ENOMEM;
 
-                        log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
+                        log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr));
                         r = dns_transaction_request_dnssec_rr(t, soa);
                         if (r < 0)
                                 return r;
@@ -1570,69 +2029,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) {
-
-        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 */
+        /* 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_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) {
@@ -1647,7 +2052,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)
@@ -1671,20 +2076,19 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
         /* Checks if the RR we are looking for must be signed with an
          * RRSIG. This is used for positive responses. */
 
-        if (t->scope->dnssec_mode != DNSSEC_YES)
+        if (t->scope->dnssec_mode == DNSSEC_NO)
                 return false;
 
         if (dns_type_is_pseudo(rr->key->type))
                 return -EINVAL;
 
-        switch (rr->key->type) {
+        r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
 
-        case DNS_TYPE_DNSKEY:
-        case DNS_TYPE_DS:
-        case DNS_TYPE_NSEC:
-        case DNS_TYPE_NSEC3:
-                /* We never consider DNSKEY, DS, NSEC, NSEC3 RRs if they aren't signed. */
-                return true;
+        switch (rr->key->type) {
 
         case DNS_TYPE_RRSIG:
                 /* RRSIGs are the signatures themselves, they need no signing. */
@@ -1695,7 +2099,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                 DnsTransaction *dt;
                 Iterator i;
 
-                /* For SOA or NS RRs we look for a matching DS transaction, or a SOA transaction of the parent */
+                /* For SOA or NS RRs we look for a matching DS transaction */
 
                 SET_FOREACH(dt, t->dnssec_transactions, i) {
 
@@ -1726,13 +2130,18 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                 return true;
         }
 
+        case DNS_TYPE_DS:
         case DNS_TYPE_CNAME:
         case DNS_TYPE_DNAME: {
                 const char *parent = NULL;
                 DnsTransaction *dt;
                 Iterator i;
 
-                /* CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA. */
+                /*
+                 * CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
+                 *
+                 * DS RRs are signed if the parent is signed, hence also look at the parent SOA
+                 */
 
                 SET_FOREACH(dt, t->dnssec_transactions, i) {
 
@@ -1747,6 +2156,9 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                                 if (r < 0)
                                         return r;
                                 if (r == 0) {
+                                        if (rr->key->type == DNS_TYPE_DS)
+                                                return true;
+
                                         /* A CNAME/DNAME without a parent? That's sooo weird. */
                                         log_debug("Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
                                         return -EBADMSG;
@@ -1769,7 +2181,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
                 DnsTransaction *dt;
                 Iterator i;
 
-                /* Any other kind of RR. Let's see if our SOA lookup was authenticated */
+                /* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
 
                 SET_FOREACH(dt, t->dnssec_transactions, i) {
 
@@ -1796,6 +2208,66 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
         }}
 }
 
+static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKey *key) {
+        DnsTransaction *dt;
+        const char *tld;
+        Iterator i;
+        int r;
+
+        /* If DNSSEC downgrade mode is on, checks whether the
+         * specified RR is one level below a TLD we have proven not to
+         * exist. In such a case we assume that this is a private
+         * domain, and permit it.
+         *
+         * This detects cases like the Fritz!Box router networks. Each
+         * Fritz!Box router serves a private "fritz.box" zone, in the
+         * non-existing TLD "box". Requests for the "fritz.box" domain
+         * are served by the router itself, while requests for the
+         * "box" domain will result in NXDOMAIN.
+         *
+         * Note that this logic is unable to detect cases where a
+         * router serves a private DNS zone directly under
+         * non-existing TLD. In such a case we cannot detect whether
+         * the TLD is supposed to exist or not, as all requests we
+         * make for it will be answered by the router's zone, and not
+         * by the root zone. */
+
+        assert(t);
+
+        if (t->scope->dnssec_mode != DNSSEC_ALLOW_DOWNGRADE)
+                return false; /* In strict DNSSEC mode what doesn't exist, doesn't exist */
+
+        tld = DNS_RESOURCE_KEY_NAME(key);
+        r = dns_name_parent(&tld);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return false; /* Already the root domain */
+
+        if (!dns_name_is_single_label(tld))
+                return false;
+
+        SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+                if (dt->key->class != key->class)
+                        continue;
+
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), tld);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                /* We found an auxiliary lookup we did for the TLD. If
+                 * that returned with NXDOMAIN, we know the TLD didn't
+                 * exist, and hence this might be a private zone. */
+
+                return dt->answer_rcode == DNS_RCODE_NXDOMAIN;
+        }
+
+        return false;
+}
+
 static int dns_transaction_requires_nsec(DnsTransaction *t) {
         DnsTransaction *dt;
         const char *name;
@@ -1807,12 +2279,30 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
         /* Checks if we need to insist on NSEC/NSEC3 RRs for proving
          * this negative reply */
 
-        if (t->scope->dnssec_mode != DNSSEC_YES)
+        if (t->scope->dnssec_mode == DNSSEC_NO)
                 return false;
 
         if (dns_type_is_pseudo(t->key->type))
                 return -EINVAL;
 
+        r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        r = dns_transaction_in_private_tld(t, t->key);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                /* The lookup is from a TLD that is proven not to
+                 * exist, and we are in downgrade mode, hence ignore
+                 * that fact that we didn't get any NSEC RRs.*/
+
+                log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", dns_transaction_key_string(t));
+                return false;
+        }
+
         name = DNS_RESOURCE_KEY_NAME(t->key);
 
         if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
@@ -1855,9 +2345,173 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
         return true;
 }
 
+static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRecord *rr) {
+        DnsResourceRecord *rrsig;
+        bool found = false;
+        int r;
+
+        /* Checks whether any of the DNSKEYs used for the RRSIGs for
+         * the specified RRset is authenticated (i.e. has a matching
+         * DS RR). */
+
+        r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        DNS_ANSWER_FOREACH(rrsig, t->answer) {
+                DnsTransaction *dt;
+                Iterator i;
+
+                r = dnssec_key_match_rrsig(rr->key, rrsig);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+                        if (dt->key->class != rr->key->class)
+                                continue;
+
+                        if (dt->key->type == DNS_TYPE_DNSKEY) {
+
+                                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        continue;
+
+                                /* OK, we found an auxiliary DNSKEY
+                                 * lookup. If that lookup is
+                                 * authenticated, report this. */
+
+                                if (dt->answer_authenticated)
+                                        return true;
+
+                                found = true;
+
+                        } else if (dt->key->type == DNS_TYPE_DS) {
+
+                                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        continue;
+
+                                /* OK, we found an auxiliary DS
+                                 * lookup. If that lookup is
+                                 * authenticated and non-zero, we
+                                 * won! */
+
+                                if (!dt->answer_authenticated)
+                                        return false;
+
+                                return dns_answer_match_key(dt->answer, dt->key, NULL);
+                        }
+                }
+        }
+
+        return found ? false : -ENXIO;
+}
+
+static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr) {
+        assert(t);
+        assert(rr);
+
+        /* We know that the root domain is signed, hence if it appears
+         * not to be signed, there's a problem with the DNS server */
+
+        return rr->key->class == DNS_CLASS_IN &&
+                dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key));
+}
+
+static int dns_transaction_check_revoked_trust_anchors(DnsTransaction *t) {
+        DnsResourceRecord *rr;
+        int r;
+
+        assert(t);
+
+        /* Maybe warn the user that we encountered a revoked DNSKEY
+         * for a key from our trust anchor. Note that we don't care
+         * whether the DNSKEY can be authenticated or not. It's
+         * sufficient if it is self-signed. */
+
+        DNS_ANSWER_FOREACH(rr, t->answer) {
+                r = dns_trust_anchor_check_revoked(&t->scope->manager->trust_anchor, rr, t->answer);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dns_transaction_invalidate_revoked_keys(DnsTransaction *t) {
+        bool changed;
+        int r;
+
+        assert(t);
+
+        /* Removes all DNSKEY/DS objects from t->validated_keys that
+         * our trust anchors database considers revoked. */
+
+        do {
+                DnsResourceRecord *rr;
+
+                changed = false;
+
+                DNS_ANSWER_FOREACH(rr, t->validated_keys) {
+                        r = dns_trust_anchor_is_revoked(&t->scope->manager->trust_anchor, rr);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) {
+                                r = dns_answer_remove_by_rr(&t->validated_keys, rr);
+                                if (r < 0)
+                                        return r;
+
+                                assert(r > 0);
+                                changed = true;
+                                break;
+                        }
+                }
+        } while (changed);
+
+        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;
-        bool dnskeys_finalized = false;
+        enum {
+                PHASE_DNSKEY,   /* Phase #1, only validate DNSKEYs */
+                PHASE_NSEC,     /* Phase #2, only validate NSEC+NSEC3 */
+                PHASE_ALL,      /* Phase #3, validate everything else */
+        } phase;
         DnsResourceRecord *rr;
         DnsAnswerFlags flags;
         int r;
@@ -1868,7 +2522,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
          * t->validated_keys, let's see which RRs we can now
          * authenticate with that. */
 
-        if (t->scope->dnssec_mode != DNSSEC_YES)
+        if (t->scope->dnssec_mode == DNSSEC_NO)
                 return 0;
 
         /* Already validated */
@@ -1886,33 +2540,83 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
         if (t->answer_source != DNS_TRANSACTION_NETWORK)
                 return 0;
 
+        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("Not validating response, server lacks DNSSEC support.");
+                return 0;
+        }
+
         log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t));
 
-        /* First see if there are DNSKEYs we already known a validated DS for. */
+        /* First, see if this response contains any revoked trust
+         * anchors we care about */
+        r = dns_transaction_check_revoked_trust_anchors(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;
 
+        /* 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. */
+        r = dns_transaction_invalidate_revoked_keys(t);
+        if (r < 0)
+                return r;
+
+        phase = PHASE_DNSKEY;
         for (;;) {
-                bool changed = false;
+                bool changed = false, have_nsec = false;
 
                 DNS_ANSWER_FOREACH(rr, t->answer) {
+                        DnsResourceRecord *rrsig = NULL;
                         DnssecResult result;
 
-                        if (rr->key->type == DNS_TYPE_RRSIG)
+                        switch (rr->key->type) {
+
+                        case DNS_TYPE_RRSIG:
                                 continue;
 
-                        r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result);
-                        if (r < 0)
-                                return r;
+                        case DNS_TYPE_DNSKEY:
+                                /* We validate DNSKEYs only in the DNSKEY and ALL phases */
+                                if (phase == PHASE_NSEC)
+                                        continue;
+                                break;
+
+                        case DNS_TYPE_NSEC:
+                        case DNS_TYPE_NSEC3:
+                                have_nsec = true;
+
+                                /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */
+                                if (phase == PHASE_DNSKEY)
+                                        continue;
 
-                        if (log_get_max_level() >= LOG_DEBUG) {
-                                _cleanup_free_ char *rrs = NULL;
+                                break;
+
+                        default:
+                                /* We validate all other RRs only in the ALL phases */
+                                if (phase != PHASE_ALL)
+                                        continue;
 
-                                (void) dns_resource_record_to_string(rr, &rrs);
-                                log_debug("Looking at %s: %s", rrs ? strstrip(rrs) : "???", dnssec_result_to_string(result));
+                                break;
                         }
 
+                        r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig);
+                        if (r < 0)
+                                return r;
+
+                        log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
+
                         if (result == DNSSEC_VALIDATED) {
 
                                 if (rr->key->type == DNS_TYPE_DNSKEY) {
@@ -1925,6 +2629,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                                         r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED);
                                         if (r < 0)
                                                 return r;
+
+                                        /* some of the DNSKEYs we just
+                                         * added might already have
+                                         * been revoked, remove them
+                                         * again in that case. */
+                                        r = dns_transaction_invalidate_revoked_keys(t);
+                                        if (r < 0)
+                                                return r;
                                 }
 
                                 /* Add the validated RRset to the new
@@ -1936,68 +2648,203 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                                 if (r < 0)
                                         return r;
 
+                                t->scope->manager->n_dnssec_secure++;
+
                                 /* Exit the loop, we dropped something from the answer, start from the beginning */
                                 changed = true;
                                 break;
+                        }
 
-                        } else if (dnskeys_finalized) {
-                                /* If we haven't read all DNSKEYs yet
-                                 * a negative result of the validation
-                                 * is irrelevant, as there might be
-                                 * more DNSKEYs coming. */
+                        /* If we haven't read all DNSKEYs yet a negative result of the validation is irrelevant, as
+                         * there might be more DNSKEYs coming. Similar, if we haven't read all NSEC/NSEC3 RRs yet, we
+                         * cannot do positive wildcard proofs yet, as those require the NSEC/NSEC3 RRs. */
+                        if (phase != PHASE_ALL)
+                                continue;
+
+                        if (result == DNSSEC_VALIDATED_WILDCARD) {
+                                bool authenticated = false;
+                                const char *source;
 
-                                if (result == DNSSEC_NO_SIGNATURE) {
-                                        r = dns_transaction_requires_rrsig(t, rr);
+                                /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3
+                                 * that no matching non-wildcard RR exists.*/
+
+                                /* First step, determine the source of synthesis */
+                                r = dns_resource_record_source(rrsig, &source);
+                                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)
+                                        result = DNSSEC_INVALID;
+                                else {
+                                        r = dns_answer_move_by_key(&validated, &t->answer, rr->key, authenticated ? (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE) : 0);
                                         if (r < 0)
                                                 return r;
-                                        if (r == 0) {
-                                                /* Data does not require signing. In that case, just copy it over,
-                                                 * but remember that this is by no means authenticated.*/
+
+                                        if (authenticated)
+                                                t->scope->manager->n_dnssec_secure++;
+                                        else
+                                                t->scope->manager->n_dnssec_insecure++;
+
+                                        /* Exit the loop, we dropped something from the answer, start from the beginning */
+                                        changed = true;
+                                        break;
+                                }
+                        }
+
+                        if (result == DNSSEC_NO_SIGNATURE) {
+                                r = dns_transaction_requires_rrsig(t, rr);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0) {
+                                        /* Data does not require signing. In that case, just copy it over,
+                                         * but remember that this is by no means authenticated.*/
+                                        r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+                                        if (r < 0)
+                                                return r;
+
+                                        t->scope->manager->n_dnssec_insecure++;
+                                        changed = true;
+                                        break;
+                                }
+
+                                r = dns_transaction_known_signed(t, rr);
+                                if (r < 0)
+                                        return r;
+                                if (r > 0) {
+                                        /* 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, t->current_feature_level);
+
+                                        if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) {
+
+                                                /* Downgrading is OK? If so, just consider the information unsigned */
+
                                                 r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
                                                 if (r < 0)
                                                         return r;
 
+                                                t->scope->manager->n_dnssec_insecure++;
                                                 changed = true;
                                                 break;
                                         }
+
+                                        /* Otherwise, fail */
+                                        t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
+                                        return 0;
                                 }
 
-                                r = dns_transaction_is_primary_response(t, rr);
+                                r = dns_transaction_in_private_tld(t, rr->key);
                                 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;
+                                        _cleanup_free_ char *s = NULL;
+
+                                        /* The data is from a TLD that is proven not to exist, and we are in downgrade
+                                         * mode, hence ignore the fact that this was not signed. */
+
+                                        (void) dns_resource_key_to_string(rr->key, &s);
+                                        log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", strna(s ? strstrip(s) : NULL));
+
+                                        r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+                                        if (r < 0)
+                                                return r;
+
+                                        t->scope->manager->n_dnssec_insecure++;
+                                        changed = true;
+                                        break;
+                                }
+                        }
+
+                        if (IN_SET(result,
+                                   DNSSEC_MISSING_KEY,
+                                   DNSSEC_SIGNATURE_EXPIRED,
+                                   DNSSEC_UNSUPPORTED_ALGORITHM)) {
+
+                                r = dns_transaction_dnskey_authenticated(t, rr);
+                                if (r < 0 && r != -ENXIO)
+                                        return r;
+                                if (r == 0) {
+                                        /* The DNSKEY transaction was not authenticated, this means there's
+                                         * no DS for this, which means it's OK if no keys are found for this signature. */
+
+                                        r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
+                                        if (r < 0)
+                                                return r;
+
+                                        t->scope->manager->n_dnssec_insecure++;
+                                        changed = true;
+                                        break;
                                 }
+                        }
 
-                                /* This is just some auxiliary
-                                 * data. Just remove the RRset and
-                                 * continue. */
-                                r = dns_answer_remove_by_key(&t->answer, rr->key);
+                        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) {
+
+                                /* 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;
+                                }
 
-                                /* Exit the loop, we dropped something from the answer, start from the beginning */
-                                changed = true;
-                                break;
+                                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;
+                                }
+
+                                /* 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. */
+                        r = dns_answer_remove_by_key(&t->answer, rr->key);
+                        if (r < 0)
+                                return r;
+
+                        /* Exit the loop, we dropped something from the answer, start from the beginning */
+                        changed = true;
+                        break;
                 }
 
+                /* Restart the inner loop as long as we managed to achieve something */
                 if (changed)
                         continue;
 
-                if (!dnskeys_finalized) {
-                        /* OK, now we know we have added all DNSKEYs
-                         * we possibly could to our validated
-                         * list. Now run the whole thing once more,
-                         * and strip everything we still cannot
-                         * validate.
-                         */
-                        dnskeys_finalized = true;
+                if (phase == PHASE_DNSKEY && have_nsec) {
+                        /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */
+                        phase = PHASE_NSEC;
+                        continue;
+                }
+
+                if (phase != PHASE_ALL) {
+                        /* OK, we processed all DNSKEYs and NSEC/NSEC3 RRs, look at all the rest now. Note that in this
+                         * third phase we start to remove RRs we couldn't validate. */
+                        phase = PHASE_ALL;
                         continue;
                 }
 
@@ -2030,9 +2877,10 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
 
         } else if (r == 0) {
                 DnssecNsecResult nr;
+                bool authenticated = false;
 
                 /* Bummer! Let's check NSEC/NSEC3 */
-                r = dnssec_test_nsec(t->answer, t->key, &nr);
+                r = dnssec_nsec_test(t->answer, t->key, &nr, &authenticated, &t->answer_nsec_ttl);
                 if (r < 0)
                         return r;
 
@@ -2043,7 +2891,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                         log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
                         t->answer_dnssec_result = DNSSEC_VALIDATED;
                         t->answer_rcode = DNS_RCODE_NXDOMAIN;
-                        t->answer_authenticated = true;
+                        t->answer_authenticated = authenticated;
                         break;
 
                 case DNSSEC_NSEC_NODATA:
@@ -2051,7 +2899,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                         log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
                         t->answer_dnssec_result = DNSSEC_VALIDATED;
                         t->answer_rcode = DNS_RCODE_SUCCESS;
-                        t->answer_authenticated = true;
+                        t->answer_authenticated = authenticated;
                         break;
 
                 case DNSSEC_NSEC_OPTOUT:
@@ -2082,6 +2930,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                         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;
                         break;
@@ -2118,6 +2967,8 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
         [DNS_TRANSACTION_RESOURCES] = "resources",
         [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",
 };
 DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
 
index fea25aab09095110cb82d852b019f079870a5173..76cf6e71dba121b12af65dfdf3e379e06a57fc73 100644 (file)
@@ -38,6 +38,8 @@ enum DnsTransactionState {
         DNS_TRANSACTION_RESOURCES,
         DNS_TRANSACTION_ABORTED,
         DNS_TRANSACTION_DNSSEC_FAILED,
+        DNS_TRANSACTION_NO_TRUST_ANCHOR,
+        DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
         _DNS_TRANSACTION_STATE_MAX,
         _DNS_TRANSACTION_STATE_INVALID = -1
 };
@@ -68,6 +70,8 @@ struct DnsTransaction {
 
         uint16_t id;
 
+        bool tried_stream:1;
+
         bool initial_jitter_scheduled:1;
         bool initial_jitter_elapsed:1;
 
@@ -77,6 +81,7 @@ struct DnsTransaction {
         int answer_rcode;
         DnssecResult answer_dnssec_result;
         DnsTransactionSource answer_source;
+        uint32_t answer_nsec_ttl;
 
         /* Indicates whether the primary answer is authenticated,
          * i.e. whether the RRs from answer which directly match the
@@ -97,17 +102,18 @@ struct DnsTransaction {
         sd_event_source *timeout_event_source;
         unsigned n_attempts;
 
+        /* UDP connection logic, if we need it */
         int dns_udp_fd;
         sd_event_source *dns_udp_event_source;
 
+        /* TCP connection logic, if we need it */
+        DnsStream *stream;
+
         /* The active server */
         DnsServer *server;
 
         /* The features of the DNS server at time of transaction start */
-        DnsServerFeatureLevel current_features;
-
-        /* TCP connection logic, if we need it */
-        DnsStream *stream;
+        DnsServerFeatureLevel current_feature_level;
 
         /* Query candidates this transaction is referenced by and that
          * shall be notified about this specific transaction
@@ -136,7 +142,7 @@ struct DnsTransaction {
 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
 DnsTransaction* dns_transaction_free(DnsTransaction *t);
 
-void dns_transaction_gc(DnsTransaction *t);
+bool dns_transaction_gc(DnsTransaction *t);
 int dns_transaction_go(DnsTransaction *t);
 
 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
index 208b7fefc4856a8612966ce64cb2e2fcf2addb3e..02d7ac91e1aef72265500bf4aa515ccfbdd921bf 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sd-messages.h>
+
 #include "alloc-util.h"
+#include "conf-files.h"
+#include "def.h"
+#include "dns-domain.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "parse-util.h"
 #include "resolved-dns-trust-anchor.h"
+#include "resolved-dns-dnssec.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+
+static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
 
-/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml */
+/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
 static const uint8_t root_digest[] =
         { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
           0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
 
-int dns_trust_anchor_load(DnsTrustAnchor *d) {
+static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
+        assert(d);
+
+        /* Returns true if there's an entry for the specified domain
+         * name in our trust anchor */
+
+        return
+                hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
+                hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
+}
+
+static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
         int r;
 
         assert(d);
 
-        r = hashmap_ensure_allocated(&d->by_key, &dns_resource_key_hash_ops);
+        r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
         if (r < 0)
                 return r;
 
-        if (hashmap_get(d->by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, ".")))
+        /* Only add the built-in trust anchor if there's neither a DS
+         * nor a DNSKEY defined for the root domain. That way users
+         * have an easy way to override the root domain DS/DNSKEY
+         * data. */
+        if (dns_trust_anchor_knows_domain_positive(d, "."))
                 return 0;
 
         /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
@@ -62,26 +92,441 @@ int dns_trust_anchor_load(DnsTrustAnchor *d) {
         if (r < 0)
                 return r;
 
-        r = hashmap_put(d->by_key, rr->key, answer);
+        r = hashmap_put(d->positive_by_key, rr->key, answer);
+        if (r < 0)
+                return r;
+
+        answer = NULL;
+        return 0;
+}
+
+static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
+
+        static const char private_domains[] =
+                /* RFC 6761 says that .test is a special domain for
+                 * testing and not to be installed in the root zone */
+                "test\0"
+
+                /* RFC 6761 says that these reverse IP lookup ranges
+                 * are for private addresses, and hence should not
+                 * show up in the root zone */
+                "10.in-addr.arpa\0"
+                "16.172.in-addr.arpa\0"
+                "17.172.in-addr.arpa\0"
+                "18.172.in-addr.arpa\0"
+                "19.172.in-addr.arpa\0"
+                "20.172.in-addr.arpa\0"
+                "21.172.in-addr.arpa\0"
+                "22.172.in-addr.arpa\0"
+                "23.172.in-addr.arpa\0"
+                "24.172.in-addr.arpa\0"
+                "25.172.in-addr.arpa\0"
+                "26.172.in-addr.arpa\0"
+                "27.172.in-addr.arpa\0"
+                "28.172.in-addr.arpa\0"
+                "29.172.in-addr.arpa\0"
+                "30.172.in-addr.arpa\0"
+                "31.172.in-addr.arpa\0"
+                "168.192.in-addr.arpa\0"
+
+                /* RFC 6762 reserves the .local domain for Multicast
+                 * DNS, it hence cannot appear in the root zone. (Note
+                 * that we by default do not route .local traffic to
+                 * DNS anyway, except when a configured search domain
+                 * suggests so.) */
+                "local\0"
+
+                /* These two are well known, popular private zone
+                 * TLDs, that are blocked from delegation, according
+                 * to:
+                 * http://icannwiki.com/Name_Collision#NGPC_Resolution
+                 *
+                 * There's also ongoing work on making this official
+                 * in an RRC:
+                 * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
+                "home\0"
+                "corp\0"
+
+                /* The following four TLDs are suggested for private
+                 * zones in RFC 6762, Appendix G, and are hence very
+                 * unlikely to be made official TLDs any day soon */
+                "lan\0"
+                "intranet\0"
+                "internal\0"
+                "private\0";
+
+        const char *name;
+        int r;
+
+        assert(d);
+
+        /* Only add the built-in trust anchor if there's no negative
+         * trust anchor defined at all. This enables easy overriding
+         * of negative trust anchors. */
+
+        if (set_size(d->negative_by_name) > 0)
+                return 0;
+
+        r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
         if (r < 0)
                 return r;
 
+        /* We add a couple of domains as default negative trust
+         * anchors, where it's very unlikely they will be installed in
+         * the root zone. If they exist they must be private, and thus
+         * unsigned. */
+
+        NULSTR_FOREACH(name, private_domains) {
+
+                if (dns_trust_anchor_knows_domain_positive(d, name))
+                        continue;
+
+                r = set_put_strdup(d->negative_by_name, name);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+        _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        DnsAnswer *old_answer = NULL;
+        const char *p = s;
+        int r;
+
+        assert(d);
+        assert(line);
+
+        r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+        if (r < 0)
+                return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
+
+        if (!dns_name_is_valid(domain)) {
+                log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
+                return -EINVAL;
+        }
+
+        r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
+        if (r < 0)
+                return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
+        if (r != 2) {
+                log_warning("Missing class or type in line %s:%u", path, line);
+                return -EINVAL;
+        }
+
+        if (!strcaseeq(class, "IN")) {
+                log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
+                return -EINVAL;
+        }
+
+        if (strcaseeq(type, "DS")) {
+                _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
+                _cleanup_free_ void *dd = NULL;
+                uint16_t kt;
+                int a, dt;
+                size_t l;
+
+                r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
+                        return -EINVAL;
+                }
+                if (r != 4) {
+                        log_warning("Missing DS parameters on line %s:%u", path, line);
+                        return -EINVAL;
+                }
+
+                r = safe_atou16(key_tag, &kt);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
+
+                a = dnssec_algorithm_from_string(algorithm);
+                if (a < 0) {
+                        log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
+                        return -EINVAL;
+                }
+
+                dt = dnssec_digest_from_string(digest_type);
+                if (dt < 0) {
+                        log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
+                        return -EINVAL;
+                }
+
+                r = unhexmem(digest, strlen(digest), &dd, &l);
+                if (r < 0) {
+                        log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
+                        return -EINVAL;
+                }
+
+                rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
+                if (!rr)
+                        return log_oom();
+
+                rr->ds.key_tag = kt;
+                rr->ds.algorithm = a;
+                rr->ds.digest_type = dt;
+                rr->ds.digest_size = l;
+                rr->ds.digest = dd;
+                dd = NULL;
+
+        } else if (strcaseeq(type, "DNSKEY")) {
+                _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
+                _cleanup_free_ void *k = NULL;
+                uint16_t f;
+                size_t l;
+                int a;
+
+                r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
+                if (r != 4) {
+                        log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
+                        return -EINVAL;
+                }
+
+                if (!streq(protocol, "3")) {
+                        log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
+                        return -EINVAL;
+                }
+
+                r = safe_atou16(flags, &f);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
+                if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) {
+                        log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line);
+                        return -EINVAL;
+                }
+                if ((f & DNSKEY_FLAG_REVOKE)) {
+                        log_warning("DNSKEY is already revoked on line %s:%u", path, line);
+                        return -EINVAL;
+                }
+
+                a = dnssec_algorithm_from_string(algorithm);
+                if (a < 0) {
+                        log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
+                        return -EINVAL;
+                }
+
+                r = unbase64mem(key, strlen(key), &k, &l);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
+
+                rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
+                if (!rr)
+                        return log_oom();
+
+                rr->dnskey.flags = f;
+                rr->dnskey.protocol = 3;
+                rr->dnskey.algorithm = a;
+                rr->dnskey.key_size = l;
+                rr->dnskey.key = k;
+                k = NULL;
+
+        } else {
+                log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
+                return -EINVAL;
+        }
+
+        if (!isempty(p)) {
+                log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
+                return -EINVAL;
+        }
+
+        r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
+        if (r < 0)
+                return log_oom();
+
+        old_answer = hashmap_get(d->positive_by_key, rr->key);
+        answer = dns_answer_ref(old_answer);
+
+        r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add trust anchor RR: %m");
+
+        r = hashmap_replace(d->positive_by_key, rr->key, answer);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add answer to trust anchor: %m");
+
+        old_answer = dns_answer_unref(old_answer);
         answer = NULL;
+
+        return 0;
+}
+
+static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
+        _cleanup_free_ char *domain = NULL;
+        const char *p = s;
+        int r;
+
+        assert(d);
+        assert(line);
+
+        r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+        if (r < 0)
+                return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
+
+        if (!dns_name_is_valid(domain)) {
+                log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
+                return -EINVAL;
+        }
+
+        if (!isempty(p)) {
+                log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
+                return -EINVAL;
+        }
+
+        r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
+        if (r < 0)
+                return log_oom();
+
+        r = set_put(d->negative_by_name, domain);
+        if (r < 0)
+                return log_oom();
+        if (r > 0)
+                domain = NULL;
+
+        return 0;
+}
+
+static int dns_trust_anchor_load_files(
+                DnsTrustAnchor *d,
+                const char *suffix,
+                int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
+
+        _cleanup_strv_free_ char **files = NULL;
+        char **f;
+        int r;
+
+        assert(d);
+        assert(suffix);
+        assert(loader);
+
+        r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
+
+        STRV_FOREACH(f, files) {
+                _cleanup_fclose_ FILE *g = NULL;
+                char line[LINE_MAX];
+                unsigned n = 0;
+
+                g = fopen(*f, "r");
+                if (!g) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        log_warning_errno(errno, "Failed to open %s: %m", *f);
+                        continue;
+                }
+
+                FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
+                        char *l;
+
+                        n++;
+
+                        l = strstrip(line);
+                        if (isempty(l))
+                                continue;
+
+                        if (*l == ';')
+                                continue;
+
+                        (void) loader(d, *f, n, l);
+                }
+        }
+
+        return 0;
+}
+
+static int domain_name_cmp(const void *a, const void *b) {
+        char **x = (char**) a, **y = (char**) b;
+
+        return dns_name_compare_func(*x, *y);
+}
+
+static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
+        DnsAnswer *a;
+        Iterator i;
+
+        assert(d);
+
+        if (hashmap_isempty(d->positive_by_key))
+                log_info("No positive trust anchors defined.");
+        else {
+                log_info("Positive Trust Anchors:");
+                HASHMAP_FOREACH(a, d->positive_by_key, i) {
+                        DnsResourceRecord *rr;
+
+                        DNS_ANSWER_FOREACH(rr, a)
+                                log_info("%s", dns_resource_record_to_string(rr));
+                }
+        }
+
+        if (set_isempty(d->negative_by_name))
+                log_info("No negative trust anchors defined.");
+        else {
+                _cleanup_free_ char **l = NULL, *j = NULL;
+
+                l = set_get_strv(d->negative_by_name);
+                if (!l)
+                        return log_oom();
+
+                qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
+
+                j = strv_join(l, " ");
+                if (!j)
+                        return log_oom();
+
+                log_info("Negative trust anchors: %s", j);
+        }
+
+        return 0;
+}
+
+int dns_trust_anchor_load(DnsTrustAnchor *d) {
+        int r;
+
+        assert(d);
+
+        /* If loading things from disk fails, we don't consider this fatal */
+        (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive);
+        (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative);
+
+        /* However, if the built-in DS fails, then we have a problem. */
+        r = dns_trust_anchor_add_builtin_positive(d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
+
+        r = dns_trust_anchor_add_builtin_negative(d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
+
+        dns_trust_anchor_dump(d);
+
         return 0;
 }
 
 void dns_trust_anchor_flush(DnsTrustAnchor *d) {
         DnsAnswer *a;
+        DnsResourceRecord *rr;
 
         assert(d);
 
-        while ((a = hashmap_steal_first(d->by_key)))
+        while ((a = hashmap_steal_first(d->positive_by_key)))
                 dns_answer_unref(a);
+        d->positive_by_key = hashmap_free(d->positive_by_key);
+
+        while ((rr = set_steal_first(d->revoked_by_rr)))
+                dns_resource_record_unref(rr);
+        d->revoked_by_rr = set_free(d->revoked_by_rr);
 
-        d->by_key = hashmap_free(d->by_key);
+        d->negative_by_name = set_free_free(d->negative_by_name);
 }
 
-int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer **ret) {
+int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
         DnsAnswer *a;
 
         assert(d);
@@ -92,10 +537,209 @@ int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer **
         if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
                 return 0;
 
-        a = hashmap_get(d->by_key, key);
+        a = hashmap_get(d->positive_by_key, key);
         if (!a)
                 return 0;
 
         *ret = dns_answer_ref(a);
         return 1;
 }
+
+int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
+        assert(d);
+        assert(name);
+
+        return set_contains(d->negative_by_name, name);
+}
+
+static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+        int r;
+
+        assert(d);
+
+        r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = set_put(d->revoked_by_rr, rr);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                dns_resource_record_ref(rr);
+
+        return r;
+}
+
+static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
+        DnsAnswer *old_answer;
+        int r;
+
+        /* Remember that this is a revoked trust anchor RR */
+        r = dns_trust_anchor_revoked_put(d, rr);
+        if (r < 0)
+                return r;
+
+        /* Remove this from the positive trust anchor */
+        old_answer = hashmap_get(d->positive_by_key, rr->key);
+        if (!old_answer)
+                return 0;
+
+        new_answer = dns_answer_ref(old_answer);
+
+        r = dns_answer_remove_by_rr(&new_answer, rr);
+        if (r <= 0)
+                return r;
+
+        /* We found the key! Warn the user */
+        log_struct(LOG_WARNING,
+                   LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
+                   LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)),
+                   "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
+                   NULL);
+
+        if (dns_answer_size(new_answer) <= 0) {
+                assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
+                dns_answer_unref(old_answer);
+                return 1;
+        }
+
+        r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
+        if (r < 0)
+                return r;
+
+        new_answer = NULL;
+        dns_answer_unref(old_answer);
+        return 1;
+}
+
+static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
+        DnsAnswer *a;
+        int r;
+
+        assert(d);
+        assert(revoked_dnskey);
+        assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
+        assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
+
+        a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
+        if (a) {
+                DnsResourceRecord *anchor;
+
+                /* First, look for the precise DNSKEY in our trust anchor database */
+
+                DNS_ANSWER_FOREACH(anchor, a) {
+
+                        if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
+                                continue;
+
+                        if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
+                                continue;
+
+                        if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
+                                continue;
+
+                        /* Note that we allow the REVOKE bit to be
+                         * different! It will be set in the revoked
+                         * key, but unset in our version of it */
+                        if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
+                                continue;
+
+                        if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
+                                continue;
+
+                        dns_trust_anchor_remove_revoked(d, anchor);
+                        break;
+                }
+        }
+
+        a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(revoked_dnskey->key)));
+        if (a) {
+                DnsResourceRecord *anchor;
+
+                /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
+
+                DNS_ANSWER_FOREACH(anchor, a) {
+
+                        /* We set mask_revoke to true here, since our
+                         * 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_by_ds(revoked_dnskey, anchor, true);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        dns_trust_anchor_remove_revoked(d, anchor);
+                        break;
+                }
+        }
+
+        return 0;
+}
+
+int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
+        DnsResourceRecord *rrsig;
+        int r;
+
+        assert(d);
+        assert(dnskey);
+
+        /* Looks if "dnskey" is a self-signed RR that has been revoked
+         * and matches one of our trust anchor entries. If so, removes
+         * it from the trust anchor and returns > 0. */
+
+        if (dnskey->key->type != DNS_TYPE_DNSKEY)
+                return 0;
+
+        /* Is this DNSKEY revoked? */
+        if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
+                return 0;
+
+        /* Could this be interesting to us at all? If not,
+         * there's no point in looking for and verifying a
+         * self-signed RRSIG. */
+        if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key)))
+                return 0;
+
+        /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
+        DNS_ANSWER_FOREACH(rrsig, rrs) {
+                DnssecResult result;
+
+                if (rrsig->key->type != DNS_TYPE_RRSIG)
+                        continue;
+
+                r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
+                if (r < 0)
+                        return r;
+                if (result != DNSSEC_VALIDATED)
+                        continue;
+
+                /* Bingo! This is a revoked self-signed DNSKEY. Let's
+                 * see if this precise one exists in our trust anchor
+                 * database, too. */
+                r = dns_trust_anchor_check_revoked_one(d, dnskey);
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        return 0;
+}
+
+int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
+        assert(d);
+
+        if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
+                return 0;
+
+        return set_contains(d->revoked_by_rr, rr);
+}
index 06f37239146a7c7d91ccc3b4217c37dbe0363a9d..5d137faae16148fc10623926b233d2b7ff981e68 100644 (file)
@@ -30,10 +30,16 @@ typedef struct DnsTrustAnchor DnsTrustAnchor;
 /* This contains a fixed database mapping domain names to DS or DNSKEY records. */
 
 struct DnsTrustAnchor {
-        Hashmap *by_key;
+        Hashmap *positive_by_key;
+        Set *negative_by_name;
+        Set *revoked_by_rr;
 };
 
 int dns_trust_anchor_load(DnsTrustAnchor *d);
 void dns_trust_anchor_flush(DnsTrustAnchor *d);
 
-int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey* key, DnsAnswer **answer);
+int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey* key, DnsAnswer **answer);
+int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name);
+
+int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs);
+int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr);
index 20c8a4da902cd3ec2b56a3aeb568faa285ca421d..f60b0bddc16ba5adb600ad8aebfe9d5da7426588 100644 (file)
@@ -471,15 +471,12 @@ return_empty:
 }
 
 void dns_zone_item_conflict(DnsZoneItem *i) {
-        _cleanup_free_ char *pretty = NULL;
-
         assert(i);
 
         if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
                 return;
 
-        dns_resource_record_to_string(i->rr, &pretty);
-        log_info("Detected conflict on %s", strna(pretty));
+        log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
 
         dns_zone_item_probe_stop(i);
 
@@ -492,8 +489,6 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
 }
 
 void dns_zone_item_notify(DnsZoneItem *i) {
-        _cleanup_free_ char *pretty = NULL;
-
         assert(i);
         assert(i->probe_transaction);
 
@@ -530,15 +525,13 @@ void dns_zone_item_notify(DnsZoneItem *i) {
                 log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
         }
 
-        dns_resource_record_to_string(i->rr, &pretty);
-        log_debug("Record %s successfully probed.", strna(pretty));
+        log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
 
         dns_zone_item_probe_stop(i);
         i->state = DNS_ZONE_ITEM_ESTABLISHED;
 }
 
 static int dns_zone_item_verify(DnsZoneItem *i) {
-        _cleanup_free_ char *pretty = NULL;
         int r;
 
         assert(i);
@@ -546,8 +539,7 @@ static int dns_zone_item_verify(DnsZoneItem *i) {
         if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
                 return 0;
 
-        dns_resource_record_to_string(i->rr, &pretty);
-        log_debug("Verifying RR %s", strna(pretty));
+        log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
 
         i->state = DNS_ZONE_ITEM_VERIFYING;
         r = dns_zone_item_probe_start(i);
@@ -632,7 +624,6 @@ void dns_zone_verify_all(DnsZone *zone) {
 void dns_zone_dump(DnsZone *zone, FILE *f) {
         Iterator iterator;
         DnsZoneItem *i;
-        int r;
 
         if (!zone)
                 return;
@@ -644,10 +635,10 @@ void dns_zone_dump(DnsZone *zone, FILE *f) {
                 DnsZoneItem *j;
 
                 LIST_FOREACH(by_key, j, i) {
-                        _cleanup_free_ char *t = NULL;
+                        const char *t;
 
-                        r = dns_resource_record_to_string(j->rr, &t);
-                        if (r < 0) {
+                        t = dns_resource_record_to_string(j->rr);
+                        if (!t) {
                                 log_oom();
                                 continue;
                         }
index c815eae850d47bba2d4d621d0756d1b1cf01c659..82f26215df6df1ff7634ba0f6298f10154a620d1 100644 (file)
@@ -14,8 +14,8 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-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_support,        0,                   offsetof(Manager, llmnr_support)
-Resolve.DNSSEC,       config_parse_dnssec,         0,                   0
+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.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 (file)
index 0000000..20352a3
--- /dev/null
@@ -0,0 +1,528 @@
+/*-*- 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', "s");
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(domains, d, l->search_domains) {
+                r = sd_bus_message_append(reply, "s", d->name);
+                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) {
+        _cleanup_free_ char **domains = NULL;
+        Link *l = userdata;
+        char **i;
+        int r;
+
+        assert(message);
+        assert(l);
+
+        r = sd_bus_message_read_strv(message, &domains);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, domains) {
+
+                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 search domain %s", *i);
+                if (dns_name_is_root(*i))
+                        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);
+
+        STRV_FOREACH(i, domains) {
+                DnsSearchDomain *d;
+
+                r = dns_search_domain_find(l->search_domains, *i, &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, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
+                        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 search 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", "as", 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", "as", 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 (file)
index 0000000..d444957
--- /dev/null
@@ -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);
index 0fe2bb30bdf23bbbf36cb755f16021e31063275c..b203f19dbbdf7dbbb0f8c120e637fd88f2698cfc 100644 (file)
@@ -46,7 +46,9 @@ int link_new(Manager *m, Link **ret, int ifindex) {
                 return -ENOMEM;
 
         l->ifindex = ifindex;
-        l->llmnr_support = SUPPORT_YES;
+        l->llmnr_support = RESOLVE_SUPPORT_YES;
+        l->mdns_support = RESOLVE_SUPPORT_NO;
+        l->dnssec_mode = _DNSSEC_MODE_INVALID;
 
         r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
         if (r < 0)
@@ -61,15 +63,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_marked(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));
@@ -84,12 +98,13 @@ Link *link_free(Link *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)
@@ -98,9 +113,9 @@ static void link_allocate_scopes(Link *l) {
         } else
                 l->unicast_scope = dns_scope_free(l->unicast_scope);
 
-        if (link_relevant(l, AF_INET) &&
-            l->llmnr_support != SUPPORT_NO &&
-            l->manager->llmnr_support != SUPPORT_NO) {
+        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) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
                         if (r < 0)
@@ -109,9 +124,9 @@ 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) &&
-            l->llmnr_support != SUPPORT_NO &&
-            l->manager->llmnr_support != SUPPORT_NO &&
+        if (link_relevant(l, AF_INET6, true) &&
+            l->llmnr_support != RESOLVE_SUPPORT_NO &&
+            l->manager->llmnr_support != RESOLVE_SUPPORT_NO &&
             socket_ipv6_is_supported()) {
                 if (!l->llmnr_ipv6_scope) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
@@ -121,9 +136,9 @@ 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) &&
-            l->mdns_support != SUPPORT_NO &&
-            l->manager->mdns_support != SUPPORT_NO) {
+        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) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
                         if (r < 0)
@@ -132,9 +147,9 @@ 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) &&
-            l->mdns_support != SUPPORT_NO &&
-            l->manager->mdns_support != SUPPORT_NO) {
+        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) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
                         if (r < 0)
@@ -233,22 +248,130 @@ static int link_update_llmnr_support(Link *l) {
         if (r < 0)
                 goto clear;
 
-        r = parse_boolean(b);
-        if (r < 0) {
-                if (streq(b, "resolve"))
-                        l->llmnr_support = SUPPORT_RESOLVE;
-                else
-                        goto clear;
+        l->llmnr_support = resolve_support_from_string(b);
+        if (l->llmnr_support < 0) {
+                r = -EINVAL;
+                goto clear;
+        }
+
+        return 0;
+
+clear:
+        l->llmnr_support = RESOLVE_SUPPORT_YES;
+        return r;
+}
+
+static int link_update_mdns_support(Link *l) {
+        _cleanup_free_ char *b = NULL;
+        int r;
+
+        assert(l);
+
+        r = sd_network_link_get_mdns(l->ifindex, &b);
+        if (r == -ENODATA) {
+                r = 0;
+                goto clear;
+        }
+        if (r < 0)
+                goto clear;
+
+        l->mdns_support = resolve_support_from_string(b);
+        if (l->mdns_support < 0) {
+                r = -EINVAL;
+                goto clear;
+        }
+
+        return 0;
+
+clear:
+        l->mdns_support = RESOLVE_SUPPORT_NO;
+        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);
+
+        r = sd_network_link_get_dnssec(l->ifindex, &m);
+        if (r == -ENODATA) {
+                r = 0;
+                goto clear;
+        }
+        if (r < 0)
+                goto clear;
+
+        mode = dnssec_mode_from_string(m);
+        if (mode < 0) {
+                r = -EINVAL;
+                goto clear;
+        }
+
+        link_set_dnssec_mode(l, mode);
+
+        return 0;
+
+clear:
+        l->dnssec_mode = _DNSSEC_MODE_INVALID;
+        return r;
+}
 
-        } else if (r > 0)
-                l->llmnr_support = SUPPORT_YES;
-        else
-                l->llmnr_support = SUPPORT_NO;
+static int link_update_dnssec_negative_trust_anchors(Link *l) {
+        _cleanup_strv_free_ char **ntas = NULL;
+        _cleanup_set_free_free_ Set *ns = NULL;
+        char **i;
+        int r;
+
+        assert(l);
+
+        r = sd_network_link_get_dnssec_negative_trust_anchors(l->ifindex, &ntas);
+        if (r == -ENODATA) {
+                r = 0;
+                goto clear;
+        }
+        if (r < 0)
+                goto clear;
+
+        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 0;
 
 clear:
-        l->llmnr_support = SUPPORT_YES;
+        l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
         return r;
 }
 
@@ -294,46 +417,109 @@ 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);
 
-        link_update_dns_servers(l);
-        link_update_llmnr_support(l);
-        link_allocate_scopes(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);
+
+        r = link_update_llmnr_support(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read LLMNR support for interface %s, ignoring: %m", l->name);
+
+        r = link_update_mdns_support(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read mDNS support for interface %s, ignoring: %m", l->name);
+
+        r = link_update_dnssec_mode(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read DNSSEC mode for interface %s, ignoring: %m", l->name);
+
+        r = link_update_dnssec_negative_trust_anchors(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read DNSSEC negative trust anchors for interface %s, ignoring: %m", l->name);
 
         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_POINTOPOINT|IFF_DORMANT))
+        if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
                 return false;
 
-        if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST))
+        if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
                 return false;
 
+        if (multicast) {
+                if (l->flags & IFF_POINTOPOINT)
+                        return false;
+
+                if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
+                        return false;
+        }
+
         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;
@@ -357,12 +543,8 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
         if (l->current_dns_server == s)
                 return s;
 
-        if (s) {
-                _cleanup_free_ char *ip = NULL;
-
-                in_addr_to_string(s->family, &s->address, &ip);
-                log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name);
-        }
+        if (s)
+                log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name);
 
         dns_server_unref(l->current_dns_server);
         l->current_dns_server = dns_server_ref(s);
@@ -398,6 +580,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;
 
@@ -459,8 +665,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a) &&
                     a->link->llmnr_ipv4_scope &&
-                    a->link->llmnr_support == SUPPORT_YES &&
-                    a->link->manager->llmnr_support == SUPPORT_YES) {
+                    a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
+                    a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
 
                         if (!a->link->manager->llmnr_host_ipv4_key) {
                                 a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname);
@@ -516,8 +722,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
                 if (!force_remove &&
                     link_address_relevant(a) &&
                     a->link->llmnr_ipv6_scope &&
-                    a->link->llmnr_support == SUPPORT_YES &&
-                    a->link->manager->llmnr_support == SUPPORT_YES) {
+                    a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
+                    a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
 
                         if (!a->link->manager->llmnr_host_ipv6_key) {
                                 a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname);
index a3b406bbc21134e7d62a9534a3f267ad14767d71..6544214b77acc8a8b37b35e98484096a040f32f5 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "in-addr-util.h"
 #include "ratelimit.h"
+#include "resolve-util.h"
 
 typedef struct Link Link;
 typedef struct LinkAddress LinkAddress;
@@ -66,8 +67,10 @@ struct Link {
         LIST_HEAD(DnsSearchDomain, search_domains);
         unsigned n_search_domains;
 
-        Support llmnr_support;
-        Support mdns_support;
+        ResolveSupport llmnr_support;
+        ResolveSupport mdns_support;
+        DnssecMode dnssec_mode;
+        Set *dnssec_negative_trust_anchors;
 
         DnsScope *unicast_scope;
         DnsScope *llmnr_ipv4_scope;
@@ -75,6 +78,8 @@ struct Link {
         DnsScope *mdns_ipv4_scope;
         DnsScope *mdns_ipv6_scope;
 
+        bool is_managed;
+
         char name[IF_NAMESIZE];
         uint32_t mtu;
 };
@@ -83,14 +88,21 @@ 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);
index ed754c38996d32c16a5fe8c5f9168ed4d96d844f..f52ab8f3843098c7821d357bda0d0413c11a1292 100644 (file)
@@ -47,7 +47,7 @@ int manager_llmnr_start(Manager *m) {
 
         assert(m);
 
-        if (m->llmnr_support == SUPPORT_NO)
+        if (m->llmnr_support == RESOLVE_SUPPORT_NO)
                 return 0;
 
         r = manager_llmnr_ipv4_udp_fd(m);
@@ -80,7 +80,7 @@ int manager_llmnr_start(Manager *m) {
 
 eaddrinuse:
         log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support.");
-        m->llmnr_support = SUPPORT_NO;
+        m->llmnr_support = RESOLVE_SUPPORT_NO;
         manager_llmnr_stop(m);
 
         return 0;
@@ -117,7 +117,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
 
                 dns_scope_process_query(scope, NULL, p);
         } else
-                log_debug("Invalid LLMNR UDP packet.");
+                log_debug("Invalid LLMNR UDP packet, ignoring.");
 
         return 0;
 }
@@ -193,6 +193,8 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) {
         if (r < 0)
                 goto fail;
 
+        (void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
+
         return m->llmnr_ipv4_udp_fd;
 
 fail:
@@ -267,10 +269,10 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
         }
 
         r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
-        if (r < 0)  {
-                r = -errno;
+        if (r < 0)
                 goto fail;
-        }
+
+        (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
 
         return m->llmnr_ipv6_udp_fd;
 
@@ -393,6 +395,8 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) {
         if (r < 0)
                 goto fail;
 
+        (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
+
         return m->llmnr_ipv4_tcp_fd;
 
 fail:
@@ -464,6 +468,8 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
         if (r < 0)
                 goto fail;
 
+        (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
+
         return m->llmnr_ipv6_tcp_fd;
 
 fail:
index 20955b3f6b9b7dc0e49761b944ee3685cb71e235..d6d75a3f78cce6e1fcb1f0af374d3bef04ead78c 100644 (file)
@@ -313,6 +313,8 @@ static int manager_network_monitor_listen(Manager *m) {
         if (r < 0)
                 return r;
 
+        (void) sd_event_source_set_description(m->network_event_source, "network-monitor");
+
         return 0;
 }
 
@@ -420,6 +422,8 @@ static int manager_watch_hostname(Manager *m) {
                         return log_error_errno(r, "Failed to add hostname event source: %m");
         }
 
+        (void) sd_event_source_set_description(m->hostname_event_source, "hostname");
+
         r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname);
         if (r < 0) {
                 log_info("Defaulting to hostname 'linux'.");
@@ -476,7 +480,9 @@ int manager_new(Manager **ret) {
         m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1;
         m->hostname_fd = -1;
 
-        m->llmnr_support = SUPPORT_YES;
+        m->llmnr_support = RESOLVE_SUPPORT_YES;
+        m->mdns_support = RESOLVE_SUPPORT_NO;
+        m->dnssec_mode = DNSSEC_NO;
         m->read_resolv_conf = true;
         m->need_builtin_fallbacks = true;
 
@@ -484,6 +490,10 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
+        r = manager_parse_config_file(m);
+        if (r < 0)
+                return r;
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -1164,9 +1174,32 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) {
         return 0;
 }
 
-static const char* const support_table[_SUPPORT_MAX] = {
-        [SUPPORT_NO] = "no",
-        [SUPPORT_YES] = "yes",
-        [SUPPORT_RESOLVE] = "resolve",
-};
-DEFINE_STRING_TABLE_LOOKUP(support, Support);
+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;
+}
index b52273403a5f6d39e1a1786e0deb97c23f0b1845..8b13074298c3b7e60282dcaaf24df1dc81c74e40 100644 (file)
 #include "hashmap.h"
 #include "list.h"
 #include "ordered-set.h"
+#include "resolve-util.h"
 
 typedef struct Manager Manager;
-typedef enum Support Support;
-
-enum Support {
-        SUPPORT_NO,
-        SUPPORT_YES,
-        SUPPORT_RESOLVE,
-        _SUPPORT_MAX,
-        _SUPPORT_INVALID = -1
-};
 
 #include "resolved-dns-query.h"
 #include "resolved-dns-search-domain.h"
@@ -53,8 +45,9 @@ enum Support {
 struct Manager {
         sd_event *event;
 
-        Support llmnr_support;
-        Support mdns_support;
+        ResolveSupport llmnr_support;
+        ResolveSupport mdns_support;
+        DnssecMode dnssec_mode;
 
         /* Network */
         Hashmap *links;
@@ -128,6 +121,9 @@ struct Manager {
         sd_bus_slot *prepare_for_sleep_slot;
 
         sd_event_source *sigusr1_event_source;
+
+        unsigned n_transactions_total;
+        unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
 };
 
 /* Manager */
@@ -163,5 +159,5 @@ 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);
 
-const char* support_to_string(Support p) _const_;
-int support_from_string(const char *s) _pure_;
+DnssecMode manager_get_dnssec_mode(Manager *m);
+bool manager_dnssec_supported(Manager *m);
index db23bc9d421d6baa96f708fc56dea26fbab0b325..d5b253d4f5bae1cc86da1774571f0fb4199174da 100644 (file)
@@ -42,7 +42,7 @@ int manager_mdns_start(Manager *m) {
 
         assert(m);
 
-        if (m->mdns_support == SUPPORT_NO)
+        if (m->mdns_support == RESOLVE_SUPPORT_NO)
                 return 0;
 
         r = manager_mdns_ipv4_fd(m);
@@ -63,7 +63,7 @@ int manager_mdns_start(Manager *m) {
 
 eaddrinuse:
         log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
-        m->mdns_support = SUPPORT_NO;
+        m->mdns_support = RESOLVE_SUPPORT_NO;
         manager_mdns_stop(m);
 
         return 0;
@@ -122,7 +122,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
                                 dns_transaction_process_reply(t, p);
                 }
 
-                dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, 0, p->family, &p->sender);
+                dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
 
         } else if (dns_packet_validate_query(p) > 0)  {
                 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
index 956f380f3c8499de4ecc6a17f29149185bb20915..7567f4c369e3f478cfa59d1acc632f641f8531e4 100644 (file)
@@ -147,16 +147,14 @@ clear:
 }
 
 static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
-        _cleanup_free_ char *t  = NULL;
-        int r;
-
         assert(s);
         assert(f);
         assert(count);
 
-        r = in_addr_to_string(s->family, &s->address, &t);
-        if (r < 0) {
-                log_warning_errno(r, "Invalid DNS address. Ignoring: %m");
+        (void) dns_server_string(s);
+
+        if (!s->server_string) {
+                log_warning("Our of memory, or invalid DNS address. Ignoring server.");
                 return;
         }
 
@@ -164,7 +162,7 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
                 fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
         (*count) ++;
 
-        fprintf(f, "nameserver %s\n", t);
+        fprintf(f, "nameserver %s\n", s->server_string);
 }
 
 static void write_resolv_conf_search(
index be406b71fe7e09ce23702c0709ef2fcbd8c25c96..472bb32764e4ebd660bfcac62424531127441323 100644 (file)
@@ -81,12 +81,6 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        r = manager_parse_config_file(m);
-        if (r < 0) {
-                log_error_errno(r, "Failed to parse configuration file: %m");
-                goto finish;
-        }
-
         r = manager_start(m);
         if (r < 0) {
                 log_error_errno(r, "Failed to start manager: %m");
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
new file mode 100644 (file)
index 0000000..caac251
--- /dev/null
@@ -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;
+}
index 5b4ee220c4351ac14ff61473d2ca9aeaaccaa571..45fe1997e2ef020634a5c884d21bdb3bf0ae3193 100644 (file)
@@ -56,7 +56,6 @@ static void test_dnssec_verify_rrset2(void) {
 
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
         DnssecResult result;
 
         nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
@@ -77,8 +76,7 @@ static void test_dnssec_verify_rrset2(void) {
         assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0);
         assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0);
 
-        assert_se(dns_resource_record_to_string(nsec, &x) >= 0);
-        log_info("NSEC: %s", x);
+        log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec)));
 
         rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
         assert_se(rrsig);
@@ -96,8 +94,7 @@ static void test_dnssec_verify_rrset2(void) {
         rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
         assert_se(rrsig->rrsig.signature);
 
-        assert_se(dns_resource_record_to_string(rrsig, &y) >= 0);
-        log_info("RRSIG: %s", y);
+        log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
 
         dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
         assert_se(dnskey);
@@ -109,12 +106,11 @@ static void test_dnssec_verify_rrset2(void) {
         dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
         assert_se(dnskey->dnskey.key);
 
-        assert_se(dns_resource_record_to_string(dnskey, &z) >= 0);
-        log_info("DNSKEY: %s", z);
-        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
+        log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
 
         assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0);
-        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0);
+        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
 
         answer = dns_answer_new(1);
         assert_se(answer);
@@ -152,7 +148,6 @@ static void test_dnssec_verify_rrset(void) {
 
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *a = NULL, *rrsig = NULL, *dnskey = NULL;
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
         DnssecResult result;
 
         a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "nAsA.gov");
@@ -160,8 +155,7 @@ static void test_dnssec_verify_rrset(void) {
 
         a->a.in_addr.s_addr = inet_addr("52.0.14.116");
 
-        assert_se(dns_resource_record_to_string(a, &x) >= 0);
-        log_info("A: %s", x);
+        log_info("A: %s", strna(dns_resource_record_to_string(a)));
 
         rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
         assert_se(rrsig);
@@ -179,8 +173,7 @@ static void test_dnssec_verify_rrset(void) {
         rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
         assert_se(rrsig->rrsig.signature);
 
-        assert_se(dns_resource_record_to_string(rrsig, &y) >= 0);
-        log_info("RRSIG: %s", y);
+        log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
 
         dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
         assert_se(dnskey);
@@ -192,12 +185,11 @@ static void test_dnssec_verify_rrset(void) {
         dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
         assert_se(dnskey->dnskey.key);
 
-        assert_se(dns_resource_record_to_string(dnskey, &z) >= 0);
-        log_info("DNSKEY: %s", z);
-        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
+        log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
 
         assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0);
-        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0);
+        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
 
         answer = dns_answer_new(1);
         assert_se(answer);
@@ -239,7 +231,6 @@ static void test_dnssec_verify_dns_key(void) {
         };
 
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL;
-        _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
 
         /* The two DS RRs in effect for nasa.gov on 2015-12-01. */
         ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov");
@@ -252,8 +243,7 @@ static void test_dnssec_verify_dns_key(void) {
         ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size);
         assert_se(ds1->ds.digest);
 
-        assert_se(dns_resource_record_to_string(ds1, &a) >= 0);
-        log_info("DS1: %s", a);
+        log_info("DS1: %s", strna(dns_resource_record_to_string(ds1)));
 
         ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV");
         assert_se(ds2);
@@ -265,8 +255,7 @@ static void test_dnssec_verify_dns_key(void) {
         ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size);
         assert_se(ds2->ds.digest);
 
-        assert_se(dns_resource_record_to_string(ds2, &b) >= 0);
-        log_info("DS2: %s", b);
+        log_info("DS2: %s", strna(dns_resource_record_to_string(ds2)));
 
         dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV");
         assert_se(dnskey);
@@ -278,12 +267,11 @@ static void test_dnssec_verify_dns_key(void) {
         dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
         assert_se(dnskey->dnskey.key);
 
-        assert_se(dns_resource_record_to_string(dnskey, &c) >= 0);
-        log_info("DNSKEY: %s", c);
-        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
+        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) > 0);
-        assert_se(dnssec_verify_dnskey(dnskey, ds2) > 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) {
@@ -310,8 +298,8 @@ static void test_dnssec_nsec3_hash(void) {
         static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE };
         static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 };
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-        _cleanup_free_ char *a = NULL, *b = NULL;
         uint8_t h[DNSSEC_HASH_SIZE_MAX];
+        _cleanup_free_ char *b = NULL;
         int k;
 
         /* The NSEC3 RR for eurid.eu on 2015-12-14. */
@@ -328,8 +316,7 @@ static void test_dnssec_nsec3_hash(void) {
         assert_se(rr->nsec3.next_hashed_name);
         rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name);
 
-        assert_se(dns_resource_record_to_string(rr, &a) >= 0);
-        log_info("NSEC3: %s", a);
+        log_info("NSEC3: %s", strna(dns_resource_record_to_string(rr)));
 
         k = dnssec_nsec3_hash(rr, "eurid.eu", &h);
         assert_se(k >= 0);
index bc12f89f02704f5ce8172b524cb7a4328442aaa5..8de1445a9617da5059428e4ffa5005f6d5c5469d 100644 (file)
@@ -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;
index 5c6dc34700c4a203323063f08220c19e87eb9c86..b9a8ee4074972b202d7043c83b3e96ede864bb9b 100644 (file)
@@ -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) {
index a5e3b6a0b5a4a45be118b57825be5e6dd0314b10..18fc8277541a82293b79b4672174aa8ba5551715 100644 (file)
@@ -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);
index c46f7d21b7a11512eb083e615aadb8682330e361..3ad409fc29af8fe11b5354e6420956539ce4fde5 100644 (file)
@@ -98,8 +98,13 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                                         ((unsigned) (n[1] - '0') * 10) +
                                         ((unsigned) (n[2] - '0'));
 
-                                /* Don't allow CC characters or anything that doesn't fit in 8bit */
-                                if (k < ' ' || k > 255 || k == 127)
+                                /* Don't allow anything that doesn't
+                                 * fit in 8bit. Note that we do allow
+                                 * control characters, as some servers
+                                 * (e.g. cloudflare) are happy to
+                                 * generate labels with them
+                                 * inside. */
+                                if (k > 255)
                                         return -EINVAL;
 
                                 if (d)
@@ -154,20 +159,24 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
                 return 0;
         }
 
-        assert(**label_terminal == '.' || **label_terminal == 0);
+        terminal = *label_terminal;
+        assert(*terminal == '.' || *terminal == 0);
 
-        /* skip current terminal character */
-        terminal = *label_terminal - 1;
+        /* Skip current terminal character (and accept domain names ending it ".") */
+        if (*terminal == 0)
+                terminal--;
+        if (terminal >= name && *terminal == '.')
+                terminal--;
 
-        /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
+        /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
         for (;;) {
                 if (terminal < name) {
-                        /* reached the first label, so indicate that there are no more */
+                        /* Reached the first label, so indicate that there are no more */
                         terminal = NULL;
                         break;
                 }
 
-                /* find the start of the last label */
+                /* Find the start of the last label */
                 if (*terminal == '.') {
                         const char *y;
                         unsigned slashes = 0;
@@ -176,7 +185,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
                                 slashes ++;
 
                         if (slashes % 2 == 0) {
-                                /* the '.' was not escaped */
+                                /* The '.' was not escaped */
                                 name = terminal + 1;
                                 break;
                         } else {
@@ -241,7 +250,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
                         *(q++) = *p;
                         sz -= 1;
 
-                } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
+                } else {
 
                         /* Everything else */
 
@@ -254,9 +263,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
 
                         sz -= 4;
-
-                } else
-                        return -EINVAL;
+                }
 
                 p++;
                 l--;
@@ -406,7 +413,6 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
 
         for (;;) {
                 char label[DNS_LABEL_MAX];
-                int k;
 
                 r = dns_label_unescape(&p, label, sizeof(label));
                 if (r < 0)
@@ -425,12 +431,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;
@@ -478,27 +478,18 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
 
         assert(p);
 
-        while (*p) {
+        for (;;) {
                 char label[DNS_LABEL_MAX+1];
-                int k;
 
                 r = dns_label_unescape(&p, label, sizeof(label));
                 if (r < 0)
                         break;
-
-                k = dns_label_undo_idna(label, r, label, sizeof(label));
-                if (k < 0)
-                        break;
-                if (k > 0)
-                        r = k;
-
                 if (r == 0)
                         break;
 
-                label[r] = 0;
-                ascii_strlower(label);
-
-                string_hash_func(label, state);
+                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 */
         }
 
         /* enforce that all names are terminated by the empty label */
@@ -507,7 +498,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);
@@ -516,7 +507,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;
@@ -526,17 +517,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)
-                        r = w;
-
-                la[r] = lb[q] = 0;
-                r = strcasecmp(la, lb);
+                r = ascii_strcasecmp_nn(la, r, lb, q);
                 if (r != 0)
                         return r;
         }
@@ -548,53 +529,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);
@@ -603,18 +566,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;
@@ -622,22 +578,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;
@@ -647,9 +594,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);
@@ -660,7 +637,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;
@@ -668,13 +645,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;
@@ -682,13 +652,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;
@@ -697,9 +660,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;
@@ -867,12 +828,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;
 
@@ -905,19 +865,11 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
                 if (r < 0)
                         return r;
 
-                if (canonical) {
-                        size_t i;
-
-                        /* Optionally, output the name in DNSSEC
-                         * canonical format, as described in RFC 4034,
-                         * section 6.2. Or in other words: in
-                         * lower-case. */
-
-                        for (i = 0; i < (size_t) r; i++) {
-                                if (out[i] >= 'A' && out[i] <= 'Z')
-                                        out[i] = out[i] - 'A' + 'a';
-                        }
-                }
+                /* Optionally, output the name in DNSSEC canonical
+                 * format, as described in RFC 4034, section 6.2. Or
+                 * in other words: in lower-case. */
+                if (canonical)
+                        ascii_strlower_n((char*) out, (size_t) r);
 
                 /* Fill label length, move forward */
                 *label_length = r;
@@ -1101,17 +1053,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;
@@ -1123,10 +1073,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;
@@ -1159,3 +1108,202 @@ finish:
 
         return 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(table);
+
+        p = name;
+        for (;;) {
+                if (n > DNS_N_LABELS_MAX)
+                        return -EINVAL;
+
+                table[n] = p;
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                n++;
+        }
+
+        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;
+        int r;
+
+        assert(name);
+
+        p = name;
+        for (;;) {
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (n >= DNS_N_LABELS_MAX)
+                        return -EINVAL;
+
+                n++;
+        }
+
+        return (int) n;
+}
+
+int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = dns_name_skip(a, n_labels, &a);
+        if (r <= 0)
+                return r;
+
+        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;
+
+                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++;
+        }
+}
+
+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;
+}
index 02b51832b667b219938904fac0596da0ec890603..40c9ee5f278b067f9d69a88f2ceeb72c62ba463b 100644 (file)
@@ -42,6 +42,9 @@
 /* Maximum length of a full hostname, on the wire, including the final NUL byte */
 #define DNS_WIRE_FOMAT_HOSTNAME_MAX 255
 
+/* Maximum number of labels per valid hostname */
+#define DNS_N_LABELS_MAX 127
+
 int dns_label_unescape(const char **name, char *dest, size_t sz);
 int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
 int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
@@ -80,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);
 
@@ -96,3 +100,13 @@ bool dns_service_name_is_valid(const char *name);
 
 int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
 int dns_service_split(const char *joined, char **name, char **type, char **domain);
+
+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);
index 692e8b83385cca475c464f248a8e176ec6629738..073a8396c5a227615849acc1c1852d5d02a664ec 100644 (file)
@@ -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)
index 37de3f7cb1847e7fa3424bfbfb8b706911fe505b..76808cbdd51c05e0ae95bc029594bb3a1978efca 100644 (file)
@@ -190,7 +190,6 @@ int generator_write_timeouts(
 
         return write_drop_in_format(dir, unit, 50, "device-timeout",
                                     "# Automatically generated by %s\n\n"
-                                    "[Unit]\nJobTimeoutSec=" USEC_FMT,
-                                    program_invocation_short_name,
-                                    u / USEC_PER_SEC);
+                                    "[Unit]\nJobTimeoutSec=%s",
+                                    program_invocation_short_name, timeout);
 }
index add1df420f6160da847d1b637e067bed36caf922..5f4c00ba83cfc9d1cb10ea6226da5994822be7ee 100644 (file)
@@ -25,7 +25,7 @@
 
 #include "sd-id128.h"
 
-/* We only support root disk discovery for x86, x86-64 and ARM for
+/* We only support root disk discovery for x86, x86-64, Itanium and ARM for
  * now, since EFI for anything else doesn't really exist, and we only
  * care for root partitions on the same disk as the EFI ESP. */
 
@@ -33,6 +33,7 @@
 #define GPT_ROOT_X86_64 SD_ID128_MAKE(4f,68,bc,e3,e8,cd,4d,b1,96,e7,fb,ca,f9,84,b7,09)
 #define GPT_ROOT_ARM    SD_ID128_MAKE(69,da,d7,10,2c,e4,4e,3c,b1,6c,21,a1,d4,9a,be,d3)
 #define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
+#define GPT_ROOT_IA64   SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
 
 #define GPT_ESP         SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
 #define GPT_SWAP        SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
 #  define GPT_ROOT_NATIVE GPT_ROOT_X86
 #endif
 
+#if defined(__ia64__)
+#  define GPT_ROOT_NATIVE GPT_ROOT_IA64
+#endif
+
 #if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
 #  define GPT_ROOT_NATIVE GPT_ROOT_ARM_64
 #  define GPT_ROOT_SECONDARY GPT_ROOT_ARM
diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c
new file mode 100644 (file)
index 0000000..bf6fc26
--- /dev/null
@@ -0,0 +1,41 @@
+/*-*- 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 "conf-parser.h"
+#include "resolve-util.h"
+#include "string-table.h"
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_support, resolve_support, ResolveSupport, "Failed to parse resolve support setting");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dnssec_mode, dnssec_mode, DnssecMode, "Failed to parse DNSSEC mode setting");
+
+static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
+        [RESOLVE_SUPPORT_NO] = "no",
+        [RESOLVE_SUPPORT_YES] = "yes",
+        [RESOLVE_SUPPORT_RESOLVE] = "resolve",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES);
+
+static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
+        [DNSSEC_NO] = "no",
+        [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade",
+        [DNSSEC_YES] = "yes",
+};
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, DnssecMode, DNSSEC_YES);
diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h
new file mode 100644 (file)
index 0000000..fd93a13
--- /dev/null
@@ -0,0 +1,62 @@
+/*-*- 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 "macro.h"
+
+typedef enum ResolveSupport ResolveSupport;
+typedef enum DnssecMode DnssecMode;
+
+enum ResolveSupport {
+        RESOLVE_SUPPORT_NO,
+        RESOLVE_SUPPORT_YES,
+        RESOLVE_SUPPORT_RESOLVE,
+        _RESOLVE_SUPPORT_MAX,
+        _RESOLVE_SUPPORT_INVALID = -1
+};
+
+enum DnssecMode {
+        /* No DNSSEC validation is done */
+        DNSSEC_NO,
+
+        /* Validate locally, if the server knows DO, but if not,
+         * don't. Don't trust the AD bit. If the server doesn't do
+         * DNSSEC properly, downgrade to non-DNSSEC operation. Of
+         * course, we then are vulnerable to a downgrade attack, but
+         * that's life and what is configured. */
+        DNSSEC_ALLOW_DOWNGRADE,
+
+        /* Insist on DNSSEC server support, and rather fail than downgrading. */
+        DNSSEC_YES,
+
+        _DNSSEC_MODE_MAX,
+        _DNSSEC_MODE_INVALID = -1
+};
+
+int config_parse_resolve_support(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_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+const char* resolve_support_to_string(ResolveSupport p) _const_;
+ResolveSupport resolve_support_from_string(const char *s) _pure_;
+
+const char* dnssec_mode_to_string(DnssecMode p) _const_;
+DnssecMode dnssec_mode_from_string(const char *s) _pure_;
index b1bbbdaadddf1ac7d26ccbccb0cd9f467611b946..bf0739e5fa09d28bbea9215807c78db6b6b90f16 100644 (file)
@@ -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);
 
index 1448d974bd4d0d6ec1418445584a4f09894542ef..eaa38c5985d950afce96869188a0ae88e8487052 100644 (file)
@@ -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);
         }
@@ -2794,7 +2794,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;
 
index 072832a91619dd67e39e4703a1fbe9ee25128af2..1183df61059e472c5587bb5dbeb6b5360367c95f 100644 (file)
@@ -86,6 +86,9 @@ _SD_BEGIN_DECLARATIONS;
 
 #define SD_MESSAGE_BOOTCHART        SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18)
 
+#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)
+
 _SD_END_DECLARATIONS;
 
 #endif
index 79b4bf9ea3c07e20be788bd717bf6613ca7b25a2..653c61a162dba935c81ff6c04d6c67be77bad16d 100644 (file)
@@ -111,6 +111,27 @@ int sd_network_link_get_ntp(int ifindex, char ***addr);
  */
 int sd_network_link_get_llmnr(int ifindex, char **llmnr);
 
+/* Indicates whether or not MulticastDNS should be enabled for the
+ * link.
+ * Possible levels of support: yes, no, resolve
+ * Possible return codes:
+ *   -ENODATA: networkd is not aware of the link
+ */
+int sd_network_link_get_mdns(int ifindex, char **mdns);
+
+/* Indicates whether or not DNSSEC should be enabled for the link
+ * Possible levels of support: yes, no, allow-downgrade
+ * Possible return codes:
+ *   -ENODATA: networkd is not aware of the link
+ */
+int sd_network_link_get_dnssec(int ifindex, char **dnssec);
+
+/* Returns the list of per-interface DNSSEC negative trust anchors
+ * Possible return codes:
+ *   -ENODATA: networkd is not aware of the link, or has no such data
+ */
+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. */
index 241b51084da79ccd8ca94ec30174824035f48d4d..eb4548a2dc4f5bdf61b11a24819a929bad0af453 100644 (file)
@@ -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);
index 675f94906b2dd89eae7e22ae82ada54ff972fe00..a7e8187f4f65169159cc4a4461a9019ccf957db6 100644 (file)
@@ -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;
 }
index fc8d3ffe0d2bb4200874928cb75ff6c52015f670..629bb63c81c3da7b9498f220541cdc8d602b0087 100644 (file)
@@ -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;
 }
index de003e251cd58dd3c3078c4c6657b092d095ca27..3b260ee75de8b21a2146a9ab49deb773ae2ec4fd 100644 (file)
@@ -140,9 +140,9 @@ static void test_dns_label_unescape_suffix(void) {
         test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
         test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo  ", "", 20, 7, 0);
         test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
-        test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
+        test_dns_label_unescape_suffix_one("..", "", "", 20, 0, -EINVAL);
         test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
-        test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
+        test_dns_label_unescape_suffix_one("foobar.", "foobar", "", 20, 6, 0);
         test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
         test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
         test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
@@ -168,7 +168,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex
 static void test_dns_label_escape(void) {
         test_dns_label_escape_one("", 0, NULL, -EINVAL);
         test_dns_label_escape_one("hallo", 5, "hallo", 5);
-        test_dns_label_escape_one("hallo", 6, NULL, -EINVAL);
+        test_dns_label_escape_one("hallo", 6, "hallo\\000", 9);
         test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31);
 }
 
@@ -190,7 +190,7 @@ static void test_dns_name_normalize(void) {
         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);
-        test_dns_name_normalize_one("\\000", NULL, -EINVAL);
+        test_dns_name_normalize_one("\\000", "\\000", 0);
         test_dns_name_normalize_one("..", NULL, -EINVAL);
         test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
         test_dns_name_normalize_one("foobar.", "foobar", 0);
@@ -216,7 +216,7 @@ static void test_dns_name_equal(void) {
         test_dns_name_equal_one("abc.def", "CBA.def", false);
         test_dns_name_equal_one("", "xxx", false);
         test_dns_name_equal_one("ab", "a", false);
-        test_dns_name_equal_one("\\000", "xxxx", -EINVAL);
+        test_dns_name_equal_one("\\000", "\\000", true);
         test_dns_name_equal_one(".", "", true);
         test_dns_name_equal_one(".", ".", true);
         test_dns_name_equal_one("..", "..", -EINVAL);
@@ -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("."));
@@ -475,6 +494,130 @@ static void test_dns_name_change_suffix(void) {
         test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
 }
 
+static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
+        const char *p = NULL;
+
+        assert_se(ret == dns_name_suffix(name, n_labels, &p));
+        assert_se(streq_ptr(p, result));
+}
+
+static void test_dns_name_suffix(void) {
+        test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0);
+        test_dns_name_suffix_one("foo.bar", 1, "bar", 1);
+        test_dns_name_suffix_one("foo.bar", 0, "", 2);
+        test_dns_name_suffix_one("foo.bar", 3, NULL, -EINVAL);
+        test_dns_name_suffix_one("foo.bar", 4, NULL, -EINVAL);
+
+        test_dns_name_suffix_one("bar", 1, "bar", 0);
+        test_dns_name_suffix_one("bar", 0, "", 1);
+        test_dns_name_suffix_one("bar", 2, NULL, -EINVAL);
+        test_dns_name_suffix_one("bar", 3, NULL, -EINVAL);
+
+        test_dns_name_suffix_one("", 0, "", 0);
+        test_dns_name_suffix_one("", 1, NULL, -EINVAL);
+        test_dns_name_suffix_one("", 2, NULL, -EINVAL);
+}
+
+static void test_dns_name_count_labels_one(const char *name, int n) {
+        assert_se(dns_name_count_labels(name) == n);
+}
+
+static void test_dns_name_count_labels(void) {
+        test_dns_name_count_labels_one("foo.bar.quux.", 3);
+        test_dns_name_count_labels_one("foo.bar.quux", 3);
+        test_dns_name_count_labels_one("foo.bar.", 2);
+        test_dns_name_count_labels_one("foo.bar", 2);
+        test_dns_name_count_labels_one("foo.", 1);
+        test_dns_name_count_labels_one("foo", 1);
+        test_dns_name_count_labels_one("", 0);
+        test_dns_name_count_labels_one(".", 0);
+        test_dns_name_count_labels_one("..", -EINVAL);
+}
+
+static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) {
+        assert_se(dns_name_equal_skip(a, n_labels, b) == ret);
+}
+
+static void test_dns_name_equal_skip(void) {
+        test_dns_name_equal_skip_one("foo", 0, "bar", 0);
+        test_dns_name_equal_skip_one("foo", 0, "foo", 1);
+        test_dns_name_equal_skip_one("foo", 1, "foo", 0);
+        test_dns_name_equal_skip_one("foo", 2, "foo", 0);
+
+        test_dns_name_equal_skip_one("foo.bar", 0, "foo.bar", 1);
+        test_dns_name_equal_skip_one("foo.bar", 1, "foo.bar", 0);
+        test_dns_name_equal_skip_one("foo.bar", 2, "foo.bar", 0);
+        test_dns_name_equal_skip_one("foo.bar", 3, "foo.bar", 0);
+
+        test_dns_name_equal_skip_one("foo.bar", 0, "bar", 0);
+        test_dns_name_equal_skip_one("foo.bar", 1, "bar", 1);
+        test_dns_name_equal_skip_one("foo.bar", 2, "bar", 0);
+        test_dns_name_equal_skip_one("foo.bar", 3, "bar", 0);
+
+        test_dns_name_equal_skip_one("foo.bar", 0, "", 0);
+        test_dns_name_equal_skip_one("foo.bar", 1, "", 0);
+        test_dns_name_equal_skip_one("foo.bar", 2, "", 1);
+        test_dns_name_equal_skip_one("foo.bar", 3, "", 0);
+
+        test_dns_name_equal_skip_one("", 0, "", 1);
+        test_dns_name_equal_skip_one("", 1, "", 0);
+        test_dns_name_equal_skip_one("", 1, "foo", 0);
+        test_dns_name_equal_skip_one("", 2, "foo", 0);
+}
+
+static void test_dns_name_compare_func(void) {
+        assert_se(dns_name_compare_func("", "") == 0);
+        assert_se(dns_name_compare_func("", ".") == 0);
+        assert_se(dns_name_compare_func(".", "") == 0);
+        assert_se(dns_name_compare_func("foo", "foo.") == 0);
+        assert_se(dns_name_compare_func("foo.", "foo") == 0);
+        assert_se(dns_name_compare_func("foo", "foo") == 0);
+        assert_se(dns_name_compare_func("foo.", "foo.") == 0);
+        assert_se(dns_name_compare_func("heise.de", "HEISE.DE.") == 0);
+
+        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();
@@ -483,6 +626,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();
@@ -495,6 +639,12 @@ int main(int argc, char *argv[]) {
         test_dns_service_join();
         test_dns_service_split();
         test_dns_name_change_suffix();
+        test_dns_name_suffix();
+        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;
 }
index 753afadb0a91b03783c3b885210484126c72a587..92857cb5e2067e432329eb92d9e56686c47bcf5c 100644 (file)
@@ -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,
index 350eaf734dd1d040829707aea5aafd06e0c452c6..94d852b3b0c5d9cb5740f32f700006c8c88662d5 100644 (file)
@@ -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;
         }
 
index 00d3ecc0de7c94f48da73bd2dc220a87ff956e1a..24bfe7a60e1fcde715f34d073abbe39045bec7d5 100644 (file)
@@ -37,7 +37,7 @@ int main(int argc, char *argv[]) {
 
         assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
         new.rlim_cur = MIN(5U, old.rlim_max);
-        new.rlim_max = MIN(10U, old.rlim_max);
+        new.rlim_max = old.rlim_max;
         assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0);
 
         assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE);
@@ -53,7 +53,7 @@ int main(int argc, char *argv[]) {
         assert_se(old.rlim_max == new.rlim_max);
 
         assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0);
-        high = RLIMIT_MAKE_CONST(old.rlim_max + 1);
+        high = RLIMIT_MAKE_CONST(old.rlim_max == RLIM_INFINITY ? old.rlim_max : old.rlim_max + 1);
         assert_se(setrlimit_closest(RLIMIT_NOFILE, &high) == 0);
         assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0);
         assert_se(new.rlim_max == old.rlim_max);
index 25444c794acbb636cfb72a509dd7b7288b84528f..12889ce8730c75bc8da6e4a7604f5245e336434d 100644 (file)
@@ -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;
 }
index 0b3630f77c7dd8eb9221ec4d0ddd3414e9a096b4..cd1e4e4698c45c342e5416e40f6b5322b2ee8bea 100644 (file)
@@ -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"
@@ -625,8 +626,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,
@@ -638,38 +639,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) {
@@ -829,7 +830,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();
index f9a759e2232f914919cc440d8304bcc625f4bf0e..bb81ff5e3a2711d06092299bba6e274d11db5834 100644 (file)
@@ -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;
index b6c95cd4524e08a8a89f12873cc9e5b8d247a181..349585b634e4ef21b29bf0ff3b72880c68ff66b6 100644 (file)
@@ -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)
index 0b1ae706e70607a39f7c77a7c29d9f49f558b8d4..018b4dc5961b3f01f3941ec8330e435b900b6a08 100644 (file)
@@ -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++) {
index 1d31829a081d50dbe3094a3af0cd582715922b7c..691ef5656d182fbb5928f5d5829e53e5bd7a422c 100644 (file)
@@ -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 */
@@ -205,12 +207,12 @@ static bool test_pointers(struct udev_device *dev,
                         /* This path is taken by VMware's USB mouse, which has
                          * absolute axes, but no touch/pressure button. */
                         is_mouse = true;
-                else if (has_touch)
+                else if (has_touch || is_direct)
                         is_touchscreen = true;
                 else if (has_joystick_axes_or_buttons)
                         is_joystick = true;
         }
-        if (has_mt_coordinates && is_direct)
+        if (has_mt_coordinates && (is_direct || has_touch))
                 is_touchscreen = true;
 
         if (has_rel_coordinates && has_mouse_button)
index e549fdbee920045409bd3f79467b2fe3f3cfb8a0..e83b8b1c12b7538a0946d06adc269a853f036ffc 100644 (file)
 
 #include "fd-util.h"
 #include "fileio.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "udev.h"
 
@@ -228,7 +229,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 +248,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 +381,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 +470,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 +524,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);
index 39ae2cc1b103e885c4dc35d0285a2ad28fac02ea..fd7936c2dc3ba4ac9c496f9197447acef2054df7 100644 (file)
@@ -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);
 }
index 60de7037062701426090fc352eef10d17890c8ef..c0f4973f93899b4604e405088573804a0c6133a9 100644 (file)
@@ -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;
index 366e7fbb8770cd2982d323bc02ef0420da5a9263..8627a81ec2248e4c4c79018a6a00507f8e148a80 100644 (file)
@@ -1652,7 +1652,8 @@ exit:
 
 int main(int argc, char *argv[]) {
         _cleanup_free_ char *cgroup = NULL;
-        int r, fd_ctrl, fd_uevent;
+        _cleanup_close_ int fd_ctrl = -1, fd_uevent = -1;
+        int r;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
index a5f4529cfdbd1c4c88aeb758a9f3f5e4efed1355..622fbe9a6d605fd31c0fa0866f726f8debf51342 100644 (file)
@@ -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;
index dada99df59a1715c3869a7db5639682224ad861b..242090c76119d249d4d5d05172a64e204b448611 100755 (executable)
@@ -77,7 +77,6 @@ EOF
 /dev/mapper/varcrypt    /var    ext3    defaults 0 1
 EOF
     ) || return 1
-    setup_nspawn_root
 
     ddebug "umount $TESTDIR/root/var"
     umount $TESTDIR/root/var
index 42d475fe2fbaef2dbe8db279d9595ac0bae56538..4252a9a75d2d815cc19b3e17f624ffb2929593dc 100755 (executable)
@@ -4,9 +4,12 @@
 # installed job.
 
 systemctl start --no-block hello-after-sleep.target
-# sleep is now running, hello/start is waiting. Verify that:
+
 systemctl list-jobs > /root/list-jobs.txt
-grep 'sleep\.service.*running' /root/list-jobs.txt || exit 1
+while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do
+    systemctl list-jobs > /root/list-jobs.txt
+done
+
 grep 'hello\.service.*waiting' /root/list-jobs.txt || exit 1
 
 # This is supposed to finish quickly, not wait for sleep to finish.
index 1ee39df432d89c22075b0c41d3c73e2d2c64b610..3a05619ad5cbe42dc72a04f7b27e169bcd6e5fad 100755 (executable)
@@ -51,5 +51,13 @@ journalctl --sync
 journalctl -b -o cat -t "$ID" >/output
 cmp /expected /output
 
+# Don't lose streams on restart
+systemctl start forever-print-hola
+sleep 3
+systemctl restart systemd-journald
+sleep 3
+systemctl stop forever-print-hola
+[[ ! -f "/i-lose-my-logs" ]]
+
 touch /testok
 exit 0
index 6c5b5cf34eecda2817a067d0710733267822569b..1a14f76060d268ec65dc503b81f7f45cfb7687b1 100755 (executable)
@@ -55,6 +55,15 @@ After=multi-user.target
 [Service]
 ExecStart=/test-journal.sh
 Type=oneshot
+EOF
+
+        cat >$initdir/etc/systemd/system/forever-print-hola.service <<EOF
+[Unit]
+Description=ForeverPrintHola service
+
+[Service]
+Type=simple
+ExecStart=/bin/sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'
 EOF
 
         cp test-journal.sh $initdir/
diff --git a/test/test-execute/exec-capabilityambientset-merge.service b/test/test-execute/exec-capabilityambientset-merge.service
new file mode 100644 (file)
index 0000000..6496438
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Test for AmbientCapabilities
+
+[Service]
+ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
+Type=oneshot
+User=nobody
+AmbientCapabilities=CAP_NET_ADMIN
+AmbientCapabilities=CAP_NET_RAW
diff --git a/test/test-execute/exec-capabilityambientset.service b/test/test-execute/exec-capabilityambientset.service
new file mode 100644 (file)
index 0000000..d63f884
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Test for AmbientCapabilities
+
+[Service]
+ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
+Type=oneshot
+User=nobody
+AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
index 9288200717d20c00fd0880bc604816abd41b801b..961a6254d8920629fd7e94940078fa6acaa92f29 100644 (file)
@@ -92,7 +92,7 @@ $KERNEL_APPEND \
 
 run_nspawn() {
     set -x
-    ../../systemd-nspawn --register=no --boot --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND
+    ../../systemd-nspawn --register=no --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND
 }
 
 setup_basic_environment() {
@@ -111,21 +111,61 @@ setup_basic_environment() {
     install_keymaps
     install_terminfo
     install_execs
+    install_fsck
     install_plymouth
     install_debug_tools
     install_ld_so_conf
     strip_binaries
     install_depmod_files
     generate_module_dependencies
-    # softlink mtab
-    ln -fs /proc/self/mounts $initdir/etc/mtab
+}
+
+install_valgrind() {
+    if ! type -p valgrind; then
+        dfatal "Failed to install valgrind"
+        exit 1
+    fi
+
+    local _valgrind_bins=$(strace -e execve valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if /^execve\("([^"]+)"/')
+    dracut_install $_valgrind_bins
+
+    local _valgrind_libs=$(LD_DEBUG=files valgrind /bin/true 2>&1 >/dev/null | perl -lne 'print $1 if m{calling init: (/.*vgpreload_.*)}')
+    dracut_install $_valgrind_libs
+
+    local _valgrind_dbg_and_supp=$(
+        strace -e open valgrind /bin/true 2>&1 >/dev/null |
+        perl -lne 'if (my ($fname) = /^open\("([^"]+).*= (?!-)\d+/) { print $fname if $fname =~ /debug|\.supp$/ }'
+    )
+    dracut_install $_valgrind_dbg_and_supp
+}
+
+create_valgrind_wrapper() {
+    local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
+    ddebug "Create $_valgrind_wrapper"
+    cat >$_valgrind_wrapper <<EOF
+#!/bin/bash
+
+exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
+EOF
+    chmod 0755 $_valgrind_wrapper
+}
+
+install_fsck() {
+    dracut_install /sbin/fsck*
+    dracut_install -o /bin/fsck*
 }
 
 install_dmevent() {
     instmods dm_crypt =crypto
     type -P dmeventd >/dev/null && dracut_install dmeventd
     inst_libdir_file "libdevmapper-event.so*"
-    inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
+    if [[ "$LOOKS_LIKE_DEBIAN" ]]; then
+        # dmsetup installs 55-dm and 60-persistent-storage-dm on Debian/Ubuntu
+        # see https://anonscm.debian.org/cgit/pkg-lvm/lvm2.git/tree/debian/patches/0007-udev.patch
+        inst_rules 55-dm.rules 60-persistent-storage-dm.rules
+    else
+        inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
+    fi
 }
 
 install_systemd() {
@@ -172,6 +212,10 @@ check_result_nspawn() {
 }
 
 strip_binaries() {
+    if [[ "$STRIP_BINARIES" = "no" ]]; then
+        ddebug "Don't strip binaries"
+        return 0
+    fi
     ddebug "Strip binaries"
     find "$initdir" -executable -not -path '*/lib/modules/*.ko' -type f | xargs strip --strip-unneeded | ddebug
 }
index 5c807228294e099a319b8d0561e3bf78820bf20d..a345ec25d43fb97d6c5b08416e88f80e0c6f3ad1 100644 (file)
@@ -16,7 +16,7 @@ Before=getty.target
 
 [Service]
 Environment=HOME=/root
-WorkingDirectory=/root
+WorkingDirectory=-/root
 ExecStart=-@SULOGIN@
 ExecStopPost=-@SYSTEMCTL@ poweroff
 Type=idle
index 8dc3cbdede3fb234778f2fbbc0ed64c3384189fc..fb390eacfedfee931ea1ad863b81268fe4e1e81b 100644 (file)
@@ -15,7 +15,7 @@ Before=shutdown.target
 
 [Service]
 Environment=HOME=/root
-WorkingDirectory=/root
+WorkingDirectory=-/root
 ExecStartPre=-/bin/plymouth --wait quit
 ExecStartPre=-/bin/echo -e 'Welcome to emergency mode! After logging in, type "journalctl -xb" to view\\nsystem logs, "systemctl reboot" to reboot, "systemctl default" or ^D to\\ntry again to boot into default mode.'
 ExecStart=-/bin/sh -c "@SULOGIN@; @SYSTEMCTL@ --job-mode=fail --no-block default"
index 0934a8751f3c9d6e77d72452430bd3af4fac0a9c..a9c8df11841ad40b81c6deb249de47a3f1ea399c 100644 (file)
@@ -10,7 +10,7 @@ Description=Create list of required static device nodes for the current kernel
 DefaultDependencies=no
 Before=sysinit.target systemd-tmpfiles-setup-dev.service
 ConditionCapability=CAP_SYS_MODULE
-ConditionPathExists=/lib/modules/%v/modules.devname
+ConditionFileNotEmpty=/lib/modules/%v/modules.devname
 
 [Service]
 Type=oneshot
index 432e4f3c8458ee7dd8423248528f107d04854fea..6c202174d302acc5de0d5f0f1b4e4bda1ef0fd50 100644 (file)
@@ -15,7 +15,7 @@ Before=shutdown.target
 
 [Service]
 Environment=HOME=/root
-WorkingDirectory=/root
+WorkingDirectory=-/root
 ExecStartPre=-/bin/plymouth quit
 ExecStartPre=-/bin/echo -e 'Welcome to emergency mode! After logging in, type "journalctl -xb" to view\\nsystem logs, "systemctl reboot" to reboot, "systemctl default" or ^D to\\nboot into default mode.'
 ExecStart=-/bin/sh -c "@SULOGIN@; @SYSTEMCTL@ --job-mode=fail --no-block default"