]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #2131 from evverx/regenerate-m4-on-reconfigure
authorLennart Poettering <lennart@poettering.net>
Tue, 12 Jan 2016 19:35:55 +0000 (20:35 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 12 Jan 2016 19:35:55 +0000 (20:35 +0100)
build-sys: regenerate %.m4 -> % on reconfigure

213 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/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.network.xml
man/systemd.special.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/tmpfiles.d.xml
po/hu.po
po/uk.po
shell-completion/bash/machinectl
shell-completion/zsh/_machinectl
src/analyze/analyze-verify.c
src/basic/bitmap.c
src/basic/capability-util.c
src/basic/capability-util.h
src/basic/cgroup-util.c
src/basic/conf-files.c
src/basic/fd-util.h
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/log.c
src/basic/macro.h
src/basic/missing.h
src/basic/signal-util.c
src/basic/siphash24.h
src/basic/socket-util.c
src/basic/string-table.h
src/basic/string-util.c
src/basic/string-util.h
src/bootchart/svg.c
src/cgtop/cgtop.c
src/core/dbus-execute.c
src/core/dbus-manager.c
src/core/dbus.c
src/core/dbus.h
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/mount-setup.c
src/core/service.c
src/core/service.h
src/core/smack-setup.c
src/core/socket.c
src/core/transaction.c
src/core/unit.c
src/hostname/hostnamed.c
src/import/import-common.c
src/import/importd.c
src/import/org.freedesktop.import1.conf
src/import/pull-dkr.c [deleted file]
src/import/pull-dkr.h [deleted file]
src/import/pull.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote.c
src/journal-remote/journal-remote.conf.in
src/journal/compress.c
src/journal/compress.h
src/journal/journal-file.c
src/journal/journald-native.c
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/mmap-cache.c
src/journal/sd-journal.c
src/journal/test-compress-benchmark.c
src/journal/test-compress.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-lldp.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-kernel.c
src/libsystemd/sd-bus/test-bus-cleanup.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-resolve/test-resolve.c
src/login/logind-dbus.c
src/login/logind-seat.c
src/machine/machine-dbus.c
src/machine/machinectl.c
src/network/networkctl.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-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-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-dns-zone.h
src/resolve/resolved-gperf.gperf
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/resolved.conf.in
src/resolve/test-dnssec.c
src/shared/bus-util.c
src/shared/dns-domain.c
src/shared/dns-domain.h
src/shared/generator.c
src/shared/gpt.h
src/shared/import-util.c
src/shared/import-util.h
src/shared/logs-show.c
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/systemd/sd-messages.h
src/systemd/sd-network.h
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-unit-file.c
src/udev/collect/collect.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/vconsole/vconsole-setup.c
sysusers.d/.gitignore
sysusers.d/systemd-remote.conf.m4 [moved from sysusers.d/systemd-remote.conf with 86% similarity]
test/TEST-04-JOURNAL/test-journal.sh
test/TEST-04-JOURNAL/test.sh
test/TEST-05-RLIMITS/Makefile [new symlink]
test/TEST-05-RLIMITS/test-rlimits.sh [new file with mode: 0755]
test/TEST-05-RLIMITS/test.sh [new file with mode: 0755]
test/test-execute/exec-capabilityambientset-merge.service [new file with mode: 0644]
test/test-execute/exec-capabilityambientset.service [new file with mode: 0644]
units/kmod-static-nodes.service.in
units/systemd-journal-gatewayd.service.in
units/systemd-journal-gatewayd.socket
units/systemd-journal-remote.service.in
units/systemd-journal-upload.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 2f619cdeca2c075721d5014c55fa7a329154d925..e895bc8cec1dd44bceee2eee533a16ec24dc0f8c 100644 (file)
@@ -841,6 +841,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 +1056,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 += \
@@ -2413,6 +2417,11 @@ nodist_sysusers_DATA = \
        sysusers.d/systemd.conf \
        sysusers.d/basic.conf
 
+if HAVE_REMOTE
+nodist_sysusers_DATA += \
+       sysusers.d/systemd-remote.conf
+endif
+
 INSTALL_DIRS += \
        $(sysusersdir)
 endif
@@ -2420,6 +2429,7 @@ endif
 EXTRA_DIST += \
        units/systemd-sysusers.service.in \
        sysusers.d/systemd.conf.m4 \
+       sysusers.d/systemd-remote.conf.m4 \
        sysusers.d/basic.conf.in
 
 # ------------------------------------------------------------------------------
@@ -3738,6 +3748,7 @@ EXTRA_DIST += \
        hwdb/sdio.ids
 
 # ------------------------------------------------------------------------------
+if ENABLE_TESTS
 TESTS += \
        test/udev-test.pl
 
@@ -3750,6 +3761,7 @@ TESTS += \
        test/sysv-generator-test.py
 endif
 endif
+endif
 
 manual_tests += \
        test-libudev \
@@ -3769,8 +3781,10 @@ test_udev_LDADD = \
        $(BLKID_LIBS) \
        $(KMOD_LIBS)
 
+if ENABLE_TESTS
 check_DATA += \
        test/sys
+endif
 
 # packed sysfs test tree
 test/sys:
@@ -3924,11 +3938,6 @@ systemd_journal_remote_CFLAGS = \
 systemd_journal_remote_LDADD += \
        $(MICROHTTPD_LIBS)
 
-if ENABLE_SYSUSERS
-dist_sysusers_DATA += \
-       sysusers.d/systemd-remote.conf
-endif
-
 if ENABLE_TMPFILES
 dist_tmpfiles_DATA += \
        tmpfiles.d/systemd-remote.conf
@@ -5020,8 +5029,6 @@ systemd_pull_SOURCES = \
        src/import/pull-raw.h \
        src/import/pull-tar.c \
        src/import/pull-tar.h \
-       src/import/pull-dkr.c \
-       src/import/pull-dkr.h \
        src/import/pull-job.c \
        src/import/pull-job.h \
        src/import/pull-common.c \
@@ -5737,6 +5744,9 @@ EXTRA_DIST += \
        test/TEST-04-JOURNAL/Makefile \
        test/TEST-04-JOURNAL/test-journal.sh \
        test/TEST-04-JOURNAL/test.sh \
+       test/TEST-05-RLIMITS/Makefile \
+       test/TEST-05-RLIMITS/test-rlimits.sh \
+       test/TEST-05-RLIMITS/test.sh \
        test/test-functions
 
 EXTRA_DIST += \
@@ -6279,14 +6289,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 bc7068a66da294cd150d5e02cdcc971a6a9284ea..bf67f8c03ed295d5b18bfb561fc86bcefc785c33 100644 (file)
--- a/README
+++ b/README
@@ -267,3 +267,8 @@ WARNINGS:
         servers if not specified otherwise at configure time. You
         really should not ship an OS or device with this default
         setting. See DISTRO_PORTING for details.
+
+ENGINEERING AND CONSULTING SERVICES:
+        Kinvolk (https://kinvolk.io) offers professional engineering
+        and consulting services for systemd. Please contact Chris Kühl
+        <chris@kinvolk.io> for more information.
diff --git a/TODO b/TODO
index a11ecdb277b94524867922a636183776ca32f2fb..369805fcee776961044f80e4456f1c6c0514bdb0 100644 (file)
--- a/TODO
+++ b/TODO
@@ -183,6 +183,7 @@ Features:
     we always process them before we process client requests
   - DNSSEC
         - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)?
+        - synthesize negative cache entries from NSEC/NSEC3 and drop explicit negative caching of authenticated answers
   - mDNS/DNS-SD
         - mDNS RR resolving
         - service registration
@@ -600,8 +601,6 @@ Features:
     removed or added to an existing machine
   - "machinectl migrate" or similar to copy a container from or to a
     difference host, via ssh
-  - man: document how update dkr images works with machinectl
-    http://lists.freedesktop.org/archives/systemd-devel/2015-February/028630.html
   - introduce systemd-nspawn-ephemeral@.service, and hook it into
     "machinectl start" with a new --ephemeral switch
   - "machinectl status" should also show internal logs of the container in
@@ -614,8 +613,6 @@ Features:
     shell in it, and marks it read-only after use
 
 * importd:
-  - dkr: support tarsum checksum verification, if it becomes reality one day...
-  - dkr: convert json bits to nspawn configuration
   - generate a nice warning if mkfs.btrfs is missing
 
 * cryptsetup:
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 ec30ff12ae32a61327b8d92a9b18124c6fab86e9..3128ca867276e0821e28fc79fc626c58e11a88c3 100644 (file)
@@ -668,11 +668,14 @@ AC_ARG_ENABLE([smack], AS_HELP_STRING([--disable-smack],[Disable optional SMACK
                 esac],
                 [have_smack=auto])
 
-if test "x${have_smack}" = xauto; then
+if test "x${have_smack}" != xno; then
+        AC_DEFINE(HAVE_SMACK, 1, [Define if SMACK is available])
         M4_DEFINES="$M4_DEFINES -DHAVE_SMACK"
         have_smack=yes
 fi
 
+AM_CONDITIONAL([HAVE_SMACK], [test "x$have_smack" = "xyes"])
+
 have_smack_run_label=no
 AC_ARG_WITH(smack-run-label,
 AS_HELP_STRING([--with-smack-run-label=STRING],
@@ -690,12 +693,6 @@ AS_HELP_STRING([--with-smack-default-process-label=STRING],
         [AC_DEFINE_UNQUOTED(SMACK_DEFAULT_PROCESS_LABEL, ["$withval"], [Default SMACK label for executed processes])],
         [])
 
-if test "x${have_smack}" = xyes ; then
-        AC_DEFINE(HAVE_SMACK, 1, [Define if SMACK is available])
-fi
-
-AM_CONDITIONAL([HAVE_SMACK], [test "x$have_smack" = "xyes"])
-
 # ------------------------------------------------------------------------------
 AC_ARG_ENABLE([gcrypt],
         AS_HELP_STRING([--disable-gcrypt],[Disable optional GCRYPT support]),
@@ -708,7 +705,7 @@ AC_ARG_ENABLE([gcrypt],
 
 if test "x${have_gcrypt}" != xno ; then
         m4_define([AM_PATH_LIBGCRYPT_FAIL],
-                [{ test "x$have_gcrypt" != xyes || AC_MSG_ERROR([*** GCRYPT headers not found.]); }]
+                [{ test "x$have_gcrypt" != xyes || AC_MSG_ERROR([*** GCRYPT/GPG-ERROR headers not found.]); }]
         )
         m4_ifdef([AM_PATH_LIBGCRYPT], [AM_PATH_LIBGCRYPT(
                         [1.4.5],
@@ -723,12 +720,22 @@ if test "x${have_gcrypt}" != xno ; then
                 [AM_PATH_LIBGCRYPT_FAIL]
         )
 
-        if test "x$have_gcrypt" = xyes ; then
-                GCRYPT_LIBS="$LIBGCRYPT_LIBS"
-                GCRYPT_CFLAGS="$LIBGCRYPT_CFLAGS"
+        have_gpg_error=no
+        m4_ifdef([AM_PATH_GPG_ERROR], [AM_PATH_GPG_ERROR(
+                        [1.12],
+                        [have_gpg_error=yes],
+                        [AM_PATH_LIBGCRYPT_FAIL]
+                )],
+                [AM_PATH_LIBGCRYPT_FAIL]
+        )
+
+        if test "x$have_gcrypt" = xyes -a "x$have_gpg_error" = xyes ; then
+                GCRYPT_LIBS="$LIBGCRYPT_LIBS $GPG_ERROR_LIBS"
+                GCRYPT_CFLAGS="$LIBGCRYPT_CFLAGS $GPG_ERROR_CFLAGS"
                 AC_DEFINE(HAVE_GCRYPT, 1, [GCRYPT available])
         else
                 have_gcrypt=no
+                have_gpg_error=no
         fi
 else
         GCRYPT_LIBS=
@@ -838,18 +845,6 @@ if test "x$enable_qrencode" != "xno"; then
 fi
 AM_CONDITIONAL(HAVE_QRENCODE, [test "$have_qrencode" = "yes"])
 
-# ------------------------------------------------------------------------------
-have_microhttpd=no
-AC_ARG_ENABLE(microhttpd, AS_HELP_STRING([--disable-microhttpd], [disable microhttpd support]))
-if test "x$enable_microhttpd" != "xno"; then
-        PKG_CHECK_MODULES(MICROHTTPD, [libmicrohttpd >= 0.9.33],
-                [AC_DEFINE(HAVE_MICROHTTPD, 1, [Define if microhttpd is available]) have_microhttpd=yes], have_microhttpd=no)
-        if test "x$have_microhttpd" = xno -a "x$enable_microhttpd" = xyes; then
-                AC_MSG_ERROR([*** microhttpd support requested but libraries not found])
-        fi
-fi
-AM_CONDITIONAL(HAVE_MICROHTTPD, [test "$have_microhttpd" = "yes"])
-
 # ------------------------------------------------------------------------------
 have_gnutls=no
 AC_ARG_ENABLE(gnutls, AS_HELP_STRING([--disable-gnutls], [disable gnutls support]))
@@ -862,18 +857,39 @@ if test "x$enable_gnutls" != "xno"; then
 fi
 AM_CONDITIONAL(HAVE_GNUTLS, [test "$have_gnutls" = "yes"])
 
+# ------------------------------------------------------------------------------
+have_microhttpd=no
+AC_ARG_ENABLE(microhttpd, AS_HELP_STRING([--disable-microhttpd], [disable microhttpd support]))
+if test "x$enable_microhttpd" != "xno"; then
+        PKG_CHECK_MODULES(MICROHTTPD, [libmicrohttpd >= 0.9.33],
+                [AC_DEFINE(HAVE_MICROHTTPD, 1, [Define if microhttpd is available])
+                 have_microhttpd=yes
+                 M4_DEFINES="$M4_DEFINES -DHAVE_MICROHTTPD"],
+                [have_microhttpd=no])
+        if test "x$have_microhttpd" = xno -a "x$enable_microhttpd" = xyes; then
+                AC_MSG_ERROR([*** microhttpd support requested but libraries not found])
+        fi
+fi
+AM_CONDITIONAL(HAVE_MICROHTTPD, [test "$have_microhttpd" = "yes"])
+
 # ------------------------------------------------------------------------------
 have_libcurl=no
 AC_ARG_ENABLE(libcurl, AS_HELP_STRING([--disable-libcurl], [disable libcurl support]))
 if test "x$enable_libcurl" != "xno"; then
         PKG_CHECK_MODULES(LIBCURL, [libcurl],
-                [AC_DEFINE(HAVE_LIBCURL, 1, [Define if libcurl is available]) have_libcurl=yes], have_libcurl=no)
+                [AC_DEFINE(HAVE_LIBCURL, 1, [Define if libcurl is available])
+                 have_libcurl=yes
+                 M4_DEFINES="$M4_DEFINES -DHAVE_LIBCURL"],
+                [have_libcurl=no])
         if test "x$have_libcurl" = xno -a "x$enable_libcurl" = xyes; then
                 AC_MSG_ERROR([*** libcurl support requested but libraries not found])
         fi
 fi
 AM_CONDITIONAL(HAVE_LIBCURL, [test "$have_libcurl" = "yes"])
 
+# ------------------------------------------------------------------------------
+AM_CONDITIONAL(HAVE_REMOTE, [test "$have_microhttpd" = "yes" -o "$have_libcurl" = "yes"])
+
 # ------------------------------------------------------------------------------
 have_libidn=no
 AC_ARG_ENABLE(libidn, AS_HELP_STRING([--disable-libidn], [Disable optional LIBIDN support]))
@@ -1421,14 +1437,6 @@ AC_ARG_ENABLE([split-usr],
                 enable_split_usr=no
         ])])
 
-AC_ARG_WITH([dkr-index-url],
-        [AS_HELP_STRING([--dkr-index-url=URL], [Specify the default index URL to use for image downloads])],
-        [DEFAULT_DKR_INDEX_URL="\"$withval\""],
-        [DEFAULT_DKR_INDEX_URL="NULL"])
-
-AC_DEFINE_UNQUOTED(DEFAULT_DKR_INDEX_URL, [$DEFAULT_DKR_INDEX_URL], [Default index URL to use for image downloads])
-AC_SUBST(DEFAULT_DKR_INDEX_URL)
-
 AS_IF([test "x${enable_split_usr}" = "xyes"], [
         AC_DEFINE(HAVE_SPLIT_USR, 1, [Define if /bin, /sbin aren't symlinks into /usr])
 ])
@@ -1597,7 +1605,6 @@ AC_MSG_RESULT([
         Maximum System UID:      ${SYSTEM_UID_MAX}
         Maximum System GID:      ${SYSTEM_GID_MAX}
         Certificate root:        ${CERTIFICATEROOT}
-        Default dkr Index        ${DEFAULT_DKR_INDEX_URL}
 
         CFLAGS:                  ${OUR_CFLAGS} ${CFLAGS}
         CPPFLAGS:                ${OUR_CPPFLAGS} ${CPPFLAGS}
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 f7a82ee26cb8b8d3cf1a2a9dae822082cc43ff80..4e846676e008779ba06932cd947d3b7210573ff5 100644 (file)
@@ -115,6 +115,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 94906abcbfe6788d5696227543f2b870f66c254f..69a1e8fa37813c6e4f8b4dce51abfd24d21fa8b1 100644 (file)
@@ -652,6 +652,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 2383d586a3bbac6170abd734490c3afa5dcfc1e9..eda1adcfca5b156bddaee9ef6e2c7044ca4b8fd2 100644 (file)
@@ -309,6 +309,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
@@ -340,8 +342,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..51271ab
--- /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 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 a 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 fc60258d0b9222e91d190a9a3cdb8933711030fb..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>
   <refsect1>
       <title>See Also</title>
       <para>
-        <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
         <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       </para>
index 0e18953700396d99846c40b18aa4aef624628e20..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
         image.</para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><option>--dkr-index-url</option></term>
-
-        <listitem><para>Specifies the index server to use for
-        downloading <literal>dkr</literal> images with the
-        <command>pull-dkr</command>. Takes a
-        <literal>http://</literal>, <literal>https://</literal>
-        URL.</para></listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><option>--format=</option></term>
 
         below.</para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><command>pull-dkr</command> <replaceable>REMOTE</replaceable> [<replaceable>NAME</replaceable>]</term>
-
-        <listitem><para>Downloads a <literal>dkr</literal> container
-        image and makes it available locally. The remote name refers
-        to a <literal>dkr</literal> container name. If omitted, the
-        local machine name is derived from the <literal>dkr</literal>
-        container name.</para>
-
-        <para>Image verification is not available for
-        <literal>dkr</literal> containers, and thus
-        <option>--verify=no</option> must always be specified with
-        this command.</para>
-
-        <para>This command downloads all (missing) layers for the
-        specified container and places them in read-only subvolumes in
-        <filename>/var/lib/machines/</filename>. A writable snapshot
-        of the newest layer is then created under the specified local
-        machine name. To omit creation of this writable snapshot, pass
-        <literal>-</literal> as local machine name.</para>
-
-        <para>The read-only layer subvolumes are prefixed with
-        <filename>.dkr-</filename>, and thus not shown by
-        <command>list-images</command>, unless <option>--all</option>
-        is passed.</para>
-
-        <para>To specify the <literal>dkr</literal> index server to
-        use for looking up the specified container, use
-        <option>--dkr-index-url=</option>.</para>
-
-        <para>Note that pressing C-c during execution of this command
-        will not abort the download. Use
-        <command>cancel-transfer</command>, described
-        below.</para></listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><command>import-tar</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
         <term><command>import-raw</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
     <para>Note that many image operations are only supported,
     efficient or atomic on btrfs file systems. Due to this, if the
     <command>pull-tar</command>, <command>pull-raw</command>,
-    <command>pull-dkr</command>, <command>import-tar</command>,
-    <command>import-raw</command> and <command>set-limit</command>
-    commands notice that <filename>/var/lib/machines</filename> is
-    empty and not located on btrfs, they will implicitly set up a
-    loopback file <filename>/var/lib/machines.raw</filename>
-    containing a btrfs file system that is mounted to
+    <command>import-tar</command>, <command>import-raw</command> and
+    <command>set-limit</command> commands notice that
+    <filename>/var/lib/machines</filename> is empty and not located on
+    btrfs, they will implicitly set up a loopback file
+    <filename>/var/lib/machines.raw</filename> containing a btrfs file
+    system that is mounted to
     <filename>/var/lib/machines</filename>. The size of this loopback
     file may be controlled dynamically with
     <command>set-limit</command>.</para>
       login prompt into the container is requested.</para>
     </example>
 
-    <example>
-      <title>Download a Fedora <literal>dkr</literal> image</title>
-
-      <programlisting># machinectl pull-dkr --verify=no mattdm/fedora
-# systemd-nspawn -M fedora</programlisting>
-
-      <para>Downloads a <literal>dkr</literal> image and opens a shell
-      in it. Note that the specified download command might require an
-      index server to be specified with the
-      <literal>--dkr-index-url=</literal>.</para>
-    </example>
-
     <example>
       <title>Exports a container image as tar file</title>
 
index 4680b6a4e5923b2d62a22f1b30c0a8f20ae5fd32..5da2d5488ef6507864287aeae34fc1aab2c3c8c8 100644 (file)
         global setting is on.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>MulticastDNS=</varname></term>
+        <listitem><para>Takes a boolean argument or
+        <literal>resolve</literal>. Controls Multicast DNS support
+        (<ulink url="https://tools.ietf.org/html/rfc6762">RFC
+        6762</ulink>) on the local host. If true, enables full
+        Multicast DNS responder and resolver support. If false,
+        disables both. If set to <literal>resolve</literal>, only
+        resolution support is enabled, but responding is
+        disabled. Note that
+        <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        also maintains per-interface Multicast DNS settings. Multicast
+        DNS will be enabled on an interface only if the per-interface
+        and the 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..eeb406ba5b0779d4335247a2d8a6aaed023ca9a2 100644 (file)
     <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
+    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
+    fire continuously unless data is read or written to the file
     descriptor in order to reset the mask of events seen.
     </para>
 
     <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_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
index b5312735d27b8bf2359b06dc05e7b98752bc7655..a2aabd3c1ac9fdecbda45b64eb1e6688d079c86c 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_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
index df38f52fc9721a06d088917090c3d57505280054..b58d740bd865d0423fb31629ab5682d524a507a1 100644 (file)
     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>
 
     <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_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
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..58d7375eac5e1426e157680d7237187bc5b514f3 100644 (file)
@@ -67,7 +67,7 @@
 
     <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
+    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
     from. The <parameter>clock</parameter> parameter specifies the clock to
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 1fb056874ce2650e4eaffeaee98b2513176127a5..a55e06059a6864b19862b25d4ee5dd02a00cad19 100644 (file)
@@ -832,7 +832,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
             output. If you are looking for computer-parsable output,
             use <command>show</command> instead. By default, this
             function only shows 10 lines of output and ellipsizes
-            lines to fit in the terminal window. This can be changes
+            lines to fit in the terminal window. This can be changed
             with <option>--lines</option> and <option>--full</option>,
             see above. In addition, <command>journalctl
             --unit=<replaceable>NAME</replaceable></command> or
@@ -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 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..126b1b5cb420ce3cef4ef93bc06067bc29d41f91 100644 (file)
         <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 5bf1f2956bb30e53736113cf922545689d74518c..3c847d74a9efb035acb41e0839751bf4d992a90e 100644 (file)
           <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 61c540278607357df32051c270b559d7c4a33359..e7829ca968f68eb44d6cc55f6b349d2e7c9bf7d9 100644 (file)
@@ -40,7 +40,7 @@ _machinectl() {
         )
 
         local -A VERBS=(
-               [STANDALONE]='list list-images pull-tar pull-raw pull-dkr import-tar import-raw export-tar export-raw list-transfers cancel-transfer'
+               [STANDALONE]='list list-images pull-tar pull-raw import-tar import-raw export-tar export-raw list-transfers cancel-transfer'
                  [MACHINES]='status show start login shell enable disable poweroff reboot terminate kill copy-to copy-from image-status show-image clone rename read-only remove set-limit'
         )
 
index 7898d7c05b614d505a3ba2100200915efb30ee77..198fa28f7b3720da78e0e78200ea14eb677177c9 100644 (file)
@@ -44,7 +44,6 @@ _available_machines() {
 
     "pull-tar:Download a TAR container image"
     "pull-raw:Download a RAW container or VM image"
-    "pull-dkr:Download a DKR container image"
     "list-transfers:Show list of downloads in progress"
     "cancel-transfer:Cancel a download"
   )
@@ -57,7 +56,7 @@ _available_machines() {
     if (( $#cmd )); then
       if (( CURRENT == 2 )); then
         case $cmd in
-          list*|cancel-transfer|pull-tar|pull-raw|pull-dkr)
+          list*|cancel-transfer|pull-tar|pull-raw)
             msg="no options" ;;
           start)
             _available_machines ;;
@@ -97,5 +96,4 @@ _arguments \
   {-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \
   '--verify=[Verification mode for downloaded images.]:verify:(no checksum signature)' \
   '--force[Download image even if already exists.]' \
-  '--dkr-index-url=[Specify the index URL to use for DKR image downloads.]' \
   '*::machinectl command:_machinectl_command'
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 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 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..3945d37c8d2884df654eff20980c4baca12b5ad2 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"
@@ -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);
 }
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 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)
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 b3954e3223bd8963ee24594e028a195ae80983a2..dcd8ae412d1b9013f4e1676f979934deb3a1caf1 100644 (file)
@@ -37,6 +37,7 @@
 #include "util.h"
 
 #ifdef ENABLE_DEBUG_HASHMAP
+#include <pthread.h>
 #include "list.h"
 #endif
 
@@ -279,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 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 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 79901a6a0626c26a708e1f8d180bf2e7230732ae..be144e157dc12ad97d216716e2271d429c9af5ef 100644 (file)
@@ -440,17 +440,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
 }
 
 bool socket_ipv6_is_supported(void) {
-        _cleanup_free_ char *l = NULL;
-
-        if (access("/sys/module/ipv6", F_OK) != 0)
+        if (access("/proc/net/sockstat6", F_OK) != 0)
                 return false;
 
-        /* If we can't check "disable" parameter, assume enabled */
-        if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
-                return true;
-
-        /* If module was loaded with disable=1 no IPv6 available */
-        return l[0] == '0';
+        return true;
 }
 
 bool socket_address_matches_fd(const SocketAddress *a, int fd) {
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..849e457439ae76504483922e0bfca6e65cb1acfe 100644 (file)
@@ -317,14 +317,33 @@ 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;
 }
index b59b9b5a710cff0f125b3c0cf705656993b1904d..1ac6bcd6f8cbc07a699faa406e51a0e0c82a369a 100644 (file)
@@ -130,7 +130,9 @@ 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);
 
 bool chars_intersect(const char *a, const char *b) _pure_;
 
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 093179c003f2612af8aff284e001862a83ada58e..c2238c8c4317e4c052d3f10160ad538ae17c92b5 100644 (file)
@@ -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(
@@ -632,21 +648,37 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitFSIZE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitDATA", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitDATASoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitSTACK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitCORE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitCORESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitRSS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitRSSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitAS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitASSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitNPROC", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitLOCKS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitNICE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitNICESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitRTPRIO", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -673,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),
index 5457b2451b92e6d1f9e7e3b60e91b767d95ebc2e..8a523cc8ac1e5902bbd0d747754ee1f0aa887577 100644 (file)
@@ -229,7 +229,10 @@ static int property_set_log_level(
         if (r < 0)
                 return r;
 
-        return log_set_max_level_from_string(t);
+        r = log_set_max_level_from_string(t);
+        if (r == 0)
+                log_info("Setting log level to %s.", t);
+        return r;
 }
 
 static int property_get_n_names(
@@ -1939,21 +1942,37 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, default_memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, default_tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitCPUSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitFSIZE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitDATA", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitDATASoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitSTACK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitCORE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitCORESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitRSS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitRSSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitAS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitASSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitNPROC", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitLOCKS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitNICE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitNICESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitRTPRIO", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("DefaultLimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultTasksMax", "t", NULL, offsetof(Manager, default_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
 
index e7ee216f0ef78e91ff9e7e7a4e989babf2d3e973..1d89b9e2502f7887b7ed565a930aea1d54379c85 100644 (file)
@@ -734,9 +734,11 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
         return 0;
 }
 
-static int bus_list_names(Manager *m, sd_bus *bus) {
+int manager_sync_bus_names(Manager *m, sd_bus *bus) {
         _cleanup_strv_free_ char **names = NULL;
-        char **i;
+        const char *name;
+        Iterator i;
+        Unit *u;
         int r;
 
         assert(m);
@@ -746,15 +748,55 @@ static int bus_list_names(Manager *m, sd_bus *bus) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get initial list of names: %m");
 
-        /* This is a bit hacky, we say the owner of the name is the
-         * name itself, because we don't want the extra traffic to
-         * figure out the real owner. */
-        STRV_FOREACH(i, names) {
-                Unit *u;
+        /* We have to synchronize the current bus names with the
+         * list of active services. To do this, walk the list of
+         * all units with bus names. */
+        HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
+                Service *s = SERVICE(u);
+
+                assert(s);
 
-                u = hashmap_get(m->watch_bus, *i);
-                if (u)
-                        UNIT_VTABLE(u)->bus_name_owner_change(u, *i, NULL, *i);
+                if (!streq_ptr(s->bus_name, name)) {
+                        log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
+                        continue;
+                }
+
+                /* Check if a service's bus name is in the list of currently
+                 * active names */
+                if (strv_contains(names, name)) {
+                        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+                        const char *unique;
+
+                        /* If it is, determine its current owner */
+                        r = sd_bus_get_name_creds(bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to get bus name owner %s: %m", name);
+                                continue;
+                        }
+
+                        r = sd_bus_creds_get_unique_name(creds, &unique);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to get unique name for %s: %m", name);
+                                continue;
+                        }
+
+                        /* Now, let's compare that to the previous bus owner, and
+                         * if it's still the same, all is fine, so just don't
+                         * bother the service. Otherwise, the name has apparently
+                         * changed, so synthesize a name owner changed signal. */
+
+                        if (!streq_ptr(unique, s->bus_name_owner))
+                                UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, unique);
+                } else {
+                        /* So, the name we're watching is not on the bus.
+                         * This either means it simply hasn't appeared yet,
+                         * or it was lost during the daemon reload.
+                         * Check if the service has a stored name owner,
+                         * and synthesize a name loss signal in this case. */
+
+                        if (s->bus_name_owner)
+                                UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, NULL);
+                }
         }
 
         return 0;
@@ -808,7 +850,9 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
         if (r < 0)
                 return log_error_errno(r, "Failed to register name: %m");
 
-        bus_list_names(m, bus);
+        r = manager_sync_bus_names(m, bus);
+        if (r < 0)
+                return r;
 
         log_debug("Successfully connected to API bus.");
         return 0;
index 4f06ad11c41818a44948fbad97b8cd67988480ed..ff761668f3078c26b4fe5234dd783bed4f6f2ef4 100644 (file)
@@ -34,6 +34,8 @@ void bus_track_serialize(sd_bus_track *t, FILE *f);
 int bus_track_deserialize_item(char ***l, const char *line);
 int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
 
+int manager_sync_bus_names(Manager *m, sd_bus *bus);
+
 int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
 
 int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
index 4f67a9de83cc4909b9d41ba36e8457141f261175..ac91568b6349d77a7a7a8ee9b38bae797d8b73c8 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) {
@@ -2413,9 +2462,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                         prefix, c->oom_score_adjust);
 
         for (i = 0; i < RLIM_NLIMITS; i++)
-                if (c->rlimit[i])
-                        fprintf(f, "%s%s: " RLIM_FMT " " RLIM_FMT "\n",
-                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur, c->rlimit[i]->rlim_max);
+                if (c->rlimit[i]) {
+                        fprintf(f, "%s%s: " RLIM_FMT "\n",
+                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
+                        fprintf(f, "%s%sSoft: " RLIM_FMT "\n",
+                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
+                }
 
         if (c->ioprio_set) {
                 _cleanup_free_ char *class_str = NULL;
@@ -2514,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);
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 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 f9de54028e017daa6e8907b5354d19ec340e682b..7a428fcccf587841e4b1ddd2ab61f3fb0d86d063 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;
@@ -644,7 +644,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
@@ -1631,14 +1631,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 34dd715e93d7b25c5e98435cabfd7202efadc676..711b0cdcee386c6370752d8ae0b30ae14557b55c 100644 (file)
@@ -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. */
@@ -1885,23 +1888,21 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                         switch (sfsi.ssi_signo - SIGRTMIN) {
 
                         case 20:
-                                log_debug("Enabling showing of status.");
                                 manager_set_show_status(m, SHOW_STATUS_YES);
                                 break;
 
                         case 21:
-                                log_debug("Disabling showing of status.");
                                 manager_set_show_status(m, SHOW_STATUS_NO);
                                 break;
 
                         case 22:
                                 log_set_max_level(LOG_DEBUG);
-                                log_notice("Setting log level to debug.");
+                                log_info("Setting log level to debug.");
                                 break;
 
                         case 23:
                                 log_set_max_level(LOG_INFO);
-                                log_notice("Setting log level to info.");
+                                log_info("Setting log level to info.");
                                 break;
 
                         case 24:
@@ -2576,6 +2577,10 @@ int manager_reload(Manager *m) {
         /* Third, fire things up! */
         manager_coldplug(m);
 
+        /* Sync current state of bus names with our set of listening units */
+        if (m->api_bus)
+                manager_sync_bus_names(m, m->api_bus);
+
         assert(m->n_reloading > 0);
         m->n_reloading--;
 
@@ -2961,6 +2966,9 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
         if (m->running_as != MANAGER_SYSTEM)
                 return;
 
+        if (m->show_status != mode)
+                log_debug("%s showing of status.",
+                          mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
         m->show_status = mode;
 
         if (mode > 0)
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 41a729c421b9ba87d99986e7755fc310ae23b7fd..c5b689a35c5548fdf53ada28ac0f23d92d480109 100644 (file)
@@ -323,6 +323,8 @@ static void service_done(Unit *u) {
                 s->bus_name = mfree(s->bus_name);
         }
 
+        s->bus_name_owner = mfree(s->bus_name_owner);
+
         s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd);
         service_close_socket_fd(s);
         service_connection_unref(s);
@@ -2122,6 +2124,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
 
         unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
         unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
+        unit_serialize_item(u, f, "bus-name-owner", s->bus_name_owner);
 
         r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
         if (r < 0)
@@ -2249,6 +2252,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         log_unit_debug(u, "Failed to parse bus-name-good value: %s", value);
                 else
                         s->bus_name_good = b;
+        } else if (streq(key, "bus-name-owner")) {
+                r = free_and_strdup(&s->bus_name_owner, value);
+                if (r < 0)
+                        log_unit_error_errno(u, r, "Unable to deserialize current bus owner %s: %m", value);
         } else if (streq(key, "status-text")) {
                 char *t;
 
@@ -3134,6 +3141,13 @@ static void service_bus_name_owner_change(
 
         s->bus_name_good = !!new_owner;
 
+        /* Track the current owner, so we can reconstruct changes after a daemon reload */
+        r = free_and_strdup(&s->bus_name_owner, new_owner);
+        if (r < 0) {
+                log_unit_error_errno(u, r, "Unable to set new bus name owner %s: %m", new_owner);
+                return;
+        }
+
         if (s->type == SERVICE_DBUS) {
 
                 /* service_enter_running() will figure out what to
index d0faad88e0affb6a9dc07d559d6b35543aba718f..19efbccfc716eb3c69512cef5ade8fbc616f4e20 100644 (file)
@@ -172,6 +172,7 @@ struct Service {
         bool reset_cpu_usage:1;
 
         char *bus_name;
+        char *bus_name_owner; /* unique name of the current owner */
 
         char *status_text;
         int status_errno;
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 7beec3644e33330c970c5be78ccde9cd5c96a86e..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"
@@ -156,14 +156,16 @@ static void socket_done(Unit *u) {
         s->tcp_congestion = mfree(s->tcp_congestion);
         s->bind_to_device = mfree(s->bind_to_device);
 
-        free(s->smack);
-        free(s->smack_ip_in);
-        free(s->smack_ip_out);
+        s->smack = mfree(s->smack);
+        s->smack_ip_in = mfree(s->smack_ip_in);
+        s->smack_ip_out = mfree(s->smack_ip_out);
 
         strv_free(s->symlinks);
 
-        free(s->user);
-        free(s->group);
+        s->user = mfree(s->user);
+        s->group = mfree(s->group);
+
+        s->fdname = mfree(s->fdname);
 
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 }
@@ -875,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 15e79d00b30c15c24e629fa5f7c985134a8b8096..2f163190e9d5535894d4bee1fe7a2bb03acb7281 100644 (file)
@@ -950,7 +950,7 @@ int transaction_add_job_and_dependencies(
                                 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
                                 if (r < 0) {
                                         log_unit_full(dep,
-                                                      r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
+                                                      r == -EBADR /* unit masked */ ? 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 d383041d3951c6cf73fdd8c89767d18515de26a6..84605fa2674f65a468c035ad894a945daa33a8fb 100644 (file)
@@ -212,7 +212,7 @@ try_dmi:
            unreliable enough, so let's not do any additional guesswork
            on top of that.
 
-           See the SMBIOS Specification 4.0 section 7.4.1 for
+           See the SMBIOS Specification 3.0 section 7.4.1 for
            details about the values listed here:
 
            https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
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 1f308b36b383b41dc9b834bc0d7e26ccaef6e508..1b777c32b693ae3f0a36246def9c39d6db3985ee 100644 (file)
@@ -55,7 +55,6 @@ typedef enum TransferType {
         TRANSFER_EXPORT_RAW,
         TRANSFER_PULL_TAR,
         TRANSFER_PULL_RAW,
-        TRANSFER_PULL_DKR,
         _TRANSFER_TYPE_MAX,
         _TRANSFER_TYPE_INVALID = -1,
 } TransferType;
@@ -74,7 +73,6 @@ struct Transfer {
         bool force_local;
         bool read_only;
 
-        char *dkr_index_url;
         char *format;
 
         pid_t pid;
@@ -117,7 +115,6 @@ static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
         [TRANSFER_EXPORT_RAW] = "export-raw",
         [TRANSFER_PULL_TAR] = "pull-tar",
         [TRANSFER_PULL_RAW] = "pull-raw",
-        [TRANSFER_PULL_DKR] = "pull-dkr",
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
@@ -134,7 +131,6 @@ static Transfer *transfer_unref(Transfer *t) {
 
         free(t->remote);
         free(t->local);
-        free(t->dkr_index_url);
         free(t->format);
         free(t->object_path);
 
@@ -383,12 +379,11 @@ static int transfer_start(Transfer *t) {
         if (t->pid == 0) {
                 const char *cmd[] = {
                         NULL, /* systemd-import, systemd-export or systemd-pull */
-                        NULL, /* tar, raw, dkr */
+                        NULL, /* tar, raw  */
                         NULL, /* --verify= */
                         NULL, /* verify argument */
                         NULL, /* maybe --force */
                         NULL, /* maybe --read-only */
-                        NULL, /* maybe --dkr-index-url */
                         NULL, /* if so: the actual URL */
                         NULL, /* maybe --format= */
                         NULL, /* if so: the actual format */
@@ -471,10 +466,8 @@ static int transfer_start(Transfer *t) {
 
                 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
                         cmd[k++] = "tar";
-                else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_EXPORT_RAW, TRANSFER_PULL_RAW))
-                        cmd[k++] = "raw";
                 else
-                        cmd[k++] = "dkr";
+                        cmd[k++] = "raw";
 
                 if (t->verify != _IMPORT_VERIFY_INVALID) {
                         cmd[k++] = "--verify";
@@ -486,11 +479,6 @@ static int transfer_start(Transfer *t) {
                 if (t->read_only)
                         cmd[k++] = "--read-only";
 
-                if (t->dkr_index_url) {
-                        cmd[k++] = "--dkr-index-url";
-                        cmd[k++] = t->dkr_index_url;
-                }
-
                 if (t->format) {
                         cmd[k++] = "--format";
                         cmd[k++] = t->format;
@@ -707,7 +695,7 @@ static int manager_new(Manager **ret) {
         return 0;
 }
 
-static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
+static Transfer *manager_find(Manager *m, TransferType type, const char *remote) {
         Transfer *t;
         Iterator i;
 
@@ -718,8 +706,7 @@ static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_ind
         HASHMAP_FOREACH(t, m->transfers, i) {
 
                 if (t->type == type &&
-                    streq_ptr(t->remote, remote) &&
-                    streq_ptr(t->dkr_index_url, dkr_index_url))
+                    streq_ptr(t->remote, remote))
                         return t;
         }
 
@@ -907,7 +894,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
 
         type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
 
-        if (manager_find(m, type, NULL, remote))
+        if (manager_find(m, type, remote))
                 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
 
         r = transfer_new(m, &t);
@@ -939,105 +926,6 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
         return sd_bus_reply_method_return(msg, "uo", id, object);
 }
 
-static int method_pull_dkr(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
-        _cleanup_(transfer_unrefp) Transfer *t = NULL;
-        const char *index_url, *remote, *tag, *local, *verify, *object;
-        Manager *m = userdata;
-        ImportVerify v;
-        int force, r;
-        uint32_t id;
-
-        assert(msg);
-        assert(m);
-
-        r = bus_verify_polkit_async(
-                        msg,
-                        CAP_SYS_ADMIN,
-                        "org.freedesktop.import1.pull",
-                        NULL,
-                        false,
-                        UID_INVALID,
-                        &m->polkit_registry,
-                        error);
-        if (r < 0)
-                return r;
-        if (r == 0)
-                return 1; /* Will call us back */
-
-        r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
-        if (r < 0)
-                return r;
-
-        if (isempty(index_url))
-                index_url = DEFAULT_DKR_INDEX_URL;
-        if (!index_url)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
-        if (!http_url_is_valid(index_url))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
-
-        if (!dkr_name_is_valid(remote))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
-
-        if (isempty(tag))
-                tag = "latest";
-        else if (!dkr_tag_is_valid(tag))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
-
-        if (isempty(local))
-                local = NULL;
-        else if (!machine_name_is_valid(local))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
-
-        if (isempty(verify))
-                v = IMPORT_VERIFY_SIGNATURE;
-        else
-                v = import_verify_from_string(verify);
-        if (v < 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
-
-        if (v != IMPORT_VERIFY_NO)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
-
-        r = setup_machine_directory((uint64_t) -1, error);
-        if (r < 0)
-                return r;
-
-        if (manager_find(m, TRANSFER_PULL_DKR, index_url, remote))
-                return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
-
-        r = transfer_new(m, &t);
-        if (r < 0)
-                return r;
-
-        t->type = TRANSFER_PULL_DKR;
-        t->verify = v;
-        t->force_local = force;
-
-        t->dkr_index_url = strdup(index_url);
-        if (!t->dkr_index_url)
-                return -ENOMEM;
-
-        t->remote = strjoin(remote, ":", tag, NULL);
-        if (!t->remote)
-                return -ENOMEM;
-
-        if (local) {
-                t->local = strdup(local);
-                if (!t->local)
-                        return -ENOMEM;
-        }
-
-        r = transfer_start(t);
-        if (r < 0)
-                return r;
-
-        object = t->object_path;
-        id = t->id;
-        t = NULL;
-
-        return sd_bus_reply_method_return(msg, "uo", id, object);
-}
-
 static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         Manager *m = userdata;
@@ -1188,7 +1076,6 @@ static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_SIGNAL("TransferNew", "uo", 0),
index ae36af422f7ab2ee76398ea47965728e3c423cc2..ed2539a03b6ee9dd904134f6ee683eef04e04d64 100644 (file)
                        send_interface="org.freedesktop.import1.Manager"
                        send_member="PullRaw"/>
 
-                <allow send_destination="org.freedesktop.import1"
-                       send_interface="org.freedesktop.import1.Manager"
-                       send_member="PullDkr"/>
-
                 <allow send_destination="org.freedesktop.import1"
                        send_interface="org.freedesktop.import1.Transfer"
                        send_member="Cancel"/>
diff --git a/src/import/pull-dkr.c b/src/import/pull-dkr.c
deleted file mode 100644 (file)
index 831470f..0000000
+++ /dev/null
@@ -1,1346 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2014 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <curl/curl.h>
-#include <sys/prctl.h>
-
-#include "sd-daemon.h"
-
-#include "alloc-util.h"
-#include "aufs-util.h"
-#include "btrfs-util.h"
-#include "curl-util.h"
-#include "fd-util.h"
-#include "fileio.h"
-#include "fs-util.h"
-#include "hostname-util.h"
-#include "import-common.h"
-#include "import-util.h"
-#include "json.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "process-util.h"
-#include "pull-common.h"
-#include "pull-dkr.h"
-#include "pull-job.h"
-#include "rm-rf.h"
-#include "string-util.h"
-#include "strv.h"
-#include "utf8.h"
-#include "web-util.h"
-
-typedef enum DkrProgress {
-        DKR_SEARCHING,
-        DKR_RESOLVING,
-        DKR_METADATA,
-        DKR_DOWNLOADING,
-        DKR_COPYING,
-} DkrProgress;
-
-struct DkrPull {
-        sd_event *event;
-        CurlGlue *glue;
-
-        char *index_protocol;
-        char *index_address;
-
-        char *index_url;
-        char *image_root;
-
-        PullJob *images_job;
-        PullJob *tags_job;
-        PullJob *ancestry_job;
-        PullJob *json_job;
-        PullJob *layer_job;
-
-        char *name;
-        char *reference;
-        char *id;
-
-        char *response_digest;
-        char *response_token;
-        char **response_registries;
-
-        char **ancestry;
-        unsigned n_ancestry;
-        unsigned current_ancestry;
-
-        DkrPullFinished on_finished;
-        void *userdata;
-
-        char *local;
-        bool force_local;
-        bool grow_machine_directory;
-
-        char *temp_path;
-        char *final_path;
-
-        pid_t tar_pid;
-};
-
-#define PROTOCOL_PREFIX "https://"
-
-#define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
-#define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
-#define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
-#define LAYERS_MAX 127
-
-static void dkr_pull_job_on_finished(PullJob *j);
-
-DkrPull* dkr_pull_unref(DkrPull *i) {
-        if (!i)
-                return NULL;
-
-        if (i->tar_pid > 1) {
-                (void) kill_and_sigcont(i->tar_pid, SIGKILL);
-                (void) wait_for_terminate(i->tar_pid, NULL);
-        }
-
-        pull_job_unref(i->images_job);
-        pull_job_unref(i->tags_job);
-        pull_job_unref(i->ancestry_job);
-        pull_job_unref(i->json_job);
-        pull_job_unref(i->layer_job);
-
-        curl_glue_unref(i->glue);
-        sd_event_unref(i->event);
-
-        if (i->temp_path) {
-                (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-                free(i->temp_path);
-        }
-
-        free(i->name);
-        free(i->reference);
-        free(i->id);
-        free(i->response_token);
-        strv_free(i->ancestry);
-        free(i->final_path);
-        free(i->index_address);
-        free(i->index_protocol);
-        free(i->index_url);
-        free(i->image_root);
-        free(i->local);
-        free(i);
-
-        return NULL;
-}
-
-int dkr_pull_new(
-                DkrPull **ret,
-                sd_event *event,
-                const char *index_url,
-                const char *image_root,
-                DkrPullFinished on_finished,
-                void *userdata) {
-
-        _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL;
-        char *e;
-        int r;
-
-        assert(ret);
-        assert(index_url);
-
-        if (!http_url_is_valid(index_url))
-                return -EINVAL;
-
-        i = new0(DkrPull, 1);
-        if (!i)
-                return -ENOMEM;
-
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        i->image_root = strdup(image_root ?: "/var/lib/machines");
-        if (!i->image_root)
-                return -ENOMEM;
-
-        i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
-
-        i->index_url = strdup(index_url);
-        if (!i->index_url)
-                return -ENOMEM;
-
-        e = endswith(i->index_url, "/");
-        if (e)
-                *e = 0;
-
-        if (event)
-                i->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&i->event);
-                if (r < 0)
-                        return r;
-        }
-
-        r = curl_glue_new(&i->glue, i->event);
-        if (r < 0)
-                return r;
-
-        i->glue->on_finished = pull_job_curl_on_finished;
-        i->glue->userdata = i;
-
-        *ret = i;
-        i = NULL;
-
-        return 0;
-}
-
-static void dkr_pull_report_progress(DkrPull *i, DkrProgress p) {
-        unsigned percent;
-
-        assert(i);
-
-        switch (p) {
-
-        case DKR_SEARCHING:
-                percent = 0;
-                if (i->images_job)
-                        percent += i->images_job->progress_percent * 5 / 100;
-                break;
-
-        case DKR_RESOLVING:
-                percent = 5;
-                if (i->tags_job)
-                        percent += i->tags_job->progress_percent * 5 / 100;
-                break;
-
-        case DKR_METADATA:
-                percent = 10;
-                if (i->ancestry_job)
-                        percent += i->ancestry_job->progress_percent * 5 / 100;
-                if (i->json_job)
-                        percent += i->json_job->progress_percent * 5 / 100;
-                break;
-
-        case DKR_DOWNLOADING:
-                percent = 20;
-                percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
-                if (i->layer_job)
-                        percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
-
-                break;
-
-        case DKR_COPYING:
-                percent = 95;
-                break;
-
-        default:
-                assert_not_reached("Unknown progress state");
-        }
-
-        sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
-        log_debug("Combined progress %u%%", percent);
-}
-
-static int parse_id(const void *payload, size_t size, char **ret) {
-        _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
-        union json_value v = {};
-        void *json_state = NULL;
-        const char *p;
-        int t;
-
-        assert(payload);
-        assert(ret);
-
-        if (size <= 0)
-                return -EBADMSG;
-
-        if (memchr(payload, 0, size))
-                return -EBADMSG;
-
-        buf = strndup(payload, size);
-        if (!buf)
-                return -ENOMEM;
-
-        p = buf;
-        t = json_tokenize(&p, &id, &v, &json_state, NULL);
-        if (t < 0)
-                return t;
-        if (t != JSON_STRING)
-                return -EBADMSG;
-
-        t = json_tokenize(&p, &other, &v, &json_state, NULL);
-        if (t < 0)
-                return t;
-        if (t != JSON_END)
-                return -EBADMSG;
-
-        if (!dkr_id_is_valid(id))
-                return -EBADMSG;
-
-        *ret = id;
-        id = NULL;
-
-        return 0;
-}
-
-static int parse_ancestry(const void *payload, size_t size, char ***ret) {
-        _cleanup_free_ char *buf = NULL;
-        void *json_state = NULL;
-        const char *p;
-        enum {
-                STATE_BEGIN,
-                STATE_ITEM,
-                STATE_COMMA,
-                STATE_END,
-        } state = STATE_BEGIN;
-        _cleanup_strv_free_ char **l = NULL;
-        size_t n = 0, allocated = 0;
-
-        if (size <= 0)
-                return -EBADMSG;
-
-        if (memchr(payload, 0, size))
-                return -EBADMSG;
-
-        buf = strndup(payload, size);
-        if (!buf)
-                return -ENOMEM;
-
-        p = buf;
-        for (;;) {
-                _cleanup_free_ char *str;
-                union json_value v = {};
-                int t;
-
-                t = json_tokenize(&p, &str, &v, &json_state, NULL);
-                if (t < 0)
-                        return t;
-
-                switch (state) {
-
-                case STATE_BEGIN:
-                        if (t == JSON_ARRAY_OPEN)
-                                state = STATE_ITEM;
-                        else
-                                return -EBADMSG;
-
-                        break;
-
-                case STATE_ITEM:
-                        if (t == JSON_STRING) {
-                                if (!dkr_id_is_valid(str))
-                                        return -EBADMSG;
-
-                                if (n+1 > LAYERS_MAX)
-                                        return -EFBIG;
-
-                                if (!GREEDY_REALLOC(l, allocated, n + 2))
-                                        return -ENOMEM;
-
-                                l[n++] = str;
-                                str = NULL;
-                                l[n] = NULL;
-
-                                state = STATE_COMMA;
-
-                        } else if (t == JSON_ARRAY_CLOSE)
-                                state = STATE_END;
-                        else
-                                return -EBADMSG;
-
-                        break;
-
-                case STATE_COMMA:
-                        if (t == JSON_COMMA)
-                                state = STATE_ITEM;
-                        else if (t == JSON_ARRAY_CLOSE)
-                                state = STATE_END;
-                        else
-                                return -EBADMSG;
-                        break;
-
-                case STATE_END:
-                        if (t == JSON_END) {
-
-                                if (strv_isempty(l))
-                                        return -EBADMSG;
-
-                                if (!strv_is_uniq(l))
-                                        return -EBADMSG;
-
-                                l = strv_reverse(l);
-
-                                *ret = l;
-                                l = NULL;
-                                return 0;
-                        } else
-                                return -EBADMSG;
-                }
-
-        }
-}
-
-static const char *dkr_pull_current_layer(DkrPull *i) {
-        assert(i);
-
-        if (strv_isempty(i->ancestry))
-                return NULL;
-
-        return i->ancestry[i->current_ancestry];
-}
-
-static const char *dkr_pull_current_base_layer(DkrPull *i) {
-        assert(i);
-
-        if (strv_isempty(i->ancestry))
-                return NULL;
-
-        if (i->current_ancestry <= 0)
-                return NULL;
-
-        return i->ancestry[i->current_ancestry-1];
-}
-
-static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
-        const char *t;
-
-        assert(i);
-        assert(j);
-
-        if (i->response_token)
-                t = strjoina("Authorization: Token ", i->response_token);
-        else
-                t = HEADER_TOKEN " true";
-
-        j->request_header = curl_slist_new("Accept: application/json", t, NULL);
-        if (!j->request_header)
-                return -ENOMEM;
-
-        return 0;
-}
-
-static int dkr_pull_add_bearer_token(DkrPull *i, PullJob *j) {
-        const char *t = NULL;
-
-        assert(i);
-        assert(j);
-
-        if (i->response_token)
-                t = strjoina("Authorization: Bearer ", i->response_token);
-        else
-                return -EINVAL;
-
-        j->request_header = curl_slist_new("Accept: application/json", t, NULL);
-        if (!j->request_header)
-                return -ENOMEM;
-
-        return 0;
-}
-
-static bool dkr_pull_is_done(DkrPull *i) {
-        assert(i);
-        assert(i->images_job);
-        if (i->images_job->state != PULL_JOB_DONE)
-                return false;
-
-        if (!i->tags_job || i->tags_job->state != PULL_JOB_DONE)
-                return false;
-
-        if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
-                return false;
-
-        if (i->json_job && i->json_job->state != PULL_JOB_DONE)
-                return false;
-
-        if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
-                return false;
-
-        if (dkr_pull_current_layer(i))
-                return false;
-
-        return true;
-}
-
-static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
-        int r;
-        _cleanup_free_ char *p = NULL;
-
-        assert(i);
-
-        if (!i->local)
-                return 0;
-
-        if (!i->final_path) {
-                i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
-                if (!i->final_path)
-                        return -ENOMEM;
-        }
-
-        if (version == DKR_PULL_V2) {
-                p = dirname_malloc(i->image_root);
-                if (!p)
-                        return -ENOMEM;
-        }
-
-        r = pull_make_local_copy(i->final_path, p ?: i->image_root, i->local, i->force_local);
-        if (r < 0)
-                return r;
-
-        if (version == DKR_PULL_V2) {
-                char **k;
-
-                STRV_FOREACH(k, i->ancestry) {
-                        _cleanup_free_ char *d;
-
-                        d = strjoin(i->image_root, "/.dkr-", *k, NULL);
-                        if (!d)
-                                return -ENOMEM;
-
-                        r = btrfs_subvol_remove(d, BTRFS_REMOVE_QUOTA);
-                        if (r < 0)
-                               return r;
-                }
-
-                r = rmdir(i->image_root);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static int dkr_pull_job_on_open_disk(PullJob *j) {
-        const char *base;
-        DkrPull *i;
-        int r;
-
-        assert(j);
-        assert(j->userdata);
-
-        i = j->userdata;
-        assert(i->layer_job == j);
-        assert(i->final_path);
-        assert(!i->temp_path);
-        assert(i->tar_pid <= 0);
-
-        r = tempfn_random(i->final_path, NULL, &i->temp_path);
-        if (r < 0)
-                return log_oom();
-
-        mkdir_parents_label(i->temp_path, 0700);
-
-        base = dkr_pull_current_base_layer(i);
-        if (base) {
-                const char *base_path;
-
-                base_path = strjoina(i->image_root, "/.dkr-", base);
-                r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_QUOTA);
-        } else
-                r = btrfs_subvol_make(i->temp_path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
-
-        (void) import_assign_pool_quota_and_warn(i->temp_path);
-
-        j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
-        if (j->disk_fd < 0)
-                return j->disk_fd;
-
-        return 0;
-}
-
-static void dkr_pull_job_on_progress(PullJob *j) {
-        DkrPull *i;
-
-        assert(j);
-        assert(j->userdata);
-
-        i = j->userdata;
-
-        dkr_pull_report_progress(
-                        i,
-                        j == i->images_job                       ? DKR_SEARCHING :
-                        j == i->tags_job                         ? DKR_RESOLVING :
-                        j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
-                                                                   DKR_DOWNLOADING);
-}
-
-static void dkr_pull_job_on_finished_v2(PullJob *j);
-
-static int dkr_pull_pull_layer_v2(DkrPull *i) {
-        _cleanup_free_ char *path = NULL;
-        const char *url, *layer = NULL;
-        int r;
-
-        assert(i);
-        assert(!i->layer_job);
-        assert(!i->temp_path);
-        assert(!i->final_path);
-
-        for (;;) {
-                layer = dkr_pull_current_layer(i);
-                if (!layer)
-                        return 0; /* no more layers */
-
-                path = strjoin(i->image_root, "/.dkr-", layer, NULL);
-                if (!path)
-                        return log_oom();
-
-                if (laccess(path, F_OK) < 0) {
-                        if (errno == ENOENT)
-                                break;
-
-                        return log_error_errno(errno, "Failed to check for container: %m");
-                }
-
-                log_info("Layer %s already exists, skipping.", layer);
-
-                i->current_ancestry++;
-
-                path = mfree(path);
-        }
-
-        log_info("Pulling layer %s...", layer);
-
-        i->final_path = path;
-        path = NULL;
-
-        url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/blobs/", layer);
-        r = pull_job_new(&i->layer_job, url, i->glue, i);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate layer job: %m");
-
-        r = dkr_pull_add_bearer_token(i, i->layer_job);
-        if (r < 0)
-                return log_oom();
-
-        i->layer_job->on_finished = dkr_pull_job_on_finished_v2;
-        i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
-        i->layer_job->on_progress = dkr_pull_job_on_progress;
-        i->layer_job->grow_machine_directory = i->grow_machine_directory;
-
-        r = pull_job_begin(i->layer_job);
-        if (r < 0)
-                return log_error_errno(r, "Failed to start layer job: %m");
-
-        return 0;
-}
-
-static int dkr_pull_pull_layer(DkrPull *i) {
-        _cleanup_free_ char *path = NULL;
-        const char *url, *layer = NULL;
-        int r;
-
-        assert(i);
-        assert(!i->layer_job);
-        assert(!i->temp_path);
-        assert(!i->final_path);
-
-        for (;;) {
-                layer = dkr_pull_current_layer(i);
-                if (!layer)
-                        return 0; /* no more layers */
-
-                path = strjoin(i->image_root, "/.dkr-", layer, NULL);
-                if (!path)
-                        return log_oom();
-
-                if (laccess(path, F_OK) < 0) {
-                        if (errno == ENOENT)
-                                break;
-
-                        return log_error_errno(errno, "Failed to check for container: %m");
-                }
-
-                log_info("Layer %s already exists, skipping.", layer);
-
-                i->current_ancestry++;
-
-                path = mfree(path);
-        }
-
-        log_info("Pulling layer %s...", layer);
-
-        i->final_path = path;
-        path = NULL;
-
-        url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
-        r = pull_job_new(&i->layer_job, url, i->glue, i);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate layer job: %m");
-
-        r = dkr_pull_add_token(i, i->layer_job);
-        if (r < 0)
-                return log_oom();
-
-        i->layer_job->on_finished = dkr_pull_job_on_finished;
-        i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
-        i->layer_job->on_progress = dkr_pull_job_on_progress;
-        i->layer_job->grow_machine_directory = i->grow_machine_directory;
-
-        r = pull_job_begin(i->layer_job);
-        if (r < 0)
-                return log_error_errno(r, "Failed to start layer job: %m");
-
-        return 0;
-}
-
-static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz)  {
-        _cleanup_free_ char *registry = NULL;
-        char *token, *digest;
-        DkrPull *i;
-        int r;
-
-        assert(j);
-        assert(j->userdata);
-
-        i = j->userdata;
-        r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
-        if (r < 0)
-                return log_oom();
-        if (r > 0) {
-                free(i->response_token);
-                i->response_token = token;
-                return 0;
-        }
-
-        r = curl_header_strdup(header, sz, HEADER_DIGEST, &digest);
-        if (r < 0)
-                return log_oom();
-        if (r > 0) {
-                free(i->response_digest);
-                i->response_digest = digest;
-                return 0;
-        }
-
-        r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
-        if (r < 0)
-                return log_oom();
-        if (r > 0) {
-                char **l, **k;
-
-                l = strv_split(registry, ",");
-                if (!l)
-                        return log_oom();
-
-                STRV_FOREACH(k, l) {
-                        if (!hostname_is_valid(*k, false)) {
-                                log_error("Registry hostname is not valid.");
-                                strv_free(l);
-                                return -EBADMSG;
-                        }
-                }
-
-                strv_free(i->response_registries);
-                i->response_registries = l;
-        }
-
-        return 0;
-}
-
-static void dkr_pull_job_on_finished_v2(PullJob *j) {
-        DkrPull *i;
-        int r;
-
-        assert(j);
-        assert(j->userdata);
-
-        i = j->userdata;
-        if (j->error != 0) {
-                if (j == i->images_job)
-                        log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
-                else if (j == i->ancestry_job)
-                        log_error_errno(j->error, "Failed to retrieve manifest.");
-                else if (j == i->json_job)
-                        log_error_errno(j->error, "Failed to retrieve json data.");
-                else
-                        log_error_errno(j->error, "Failed to retrieve layer data.");
-
-                r = j->error;
-                goto finish;
-        }
-
-        if (i->images_job == j) {
-                const char *url;
-
-                assert(!i->tags_job);
-                assert(!i->ancestry_job);
-                assert(!i->json_job);
-                assert(!i->layer_job);
-
-                if (strv_isempty(i->response_registries)) {
-                        r = -EBADMSG;
-                        log_error("Didn't get registry information.");
-                        goto finish;
-                }
-
-                log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
-                dkr_pull_report_progress(i, DKR_RESOLVING);
-
-                url = strjoina(i->index_protocol, "auth.", i->index_address, "/v2/token/?scope=repository:",
-                               i->name, ":pull&service=registry.", i->index_address);
-                r = pull_job_new(&i->tags_job, url, i->glue, i);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to allocate tags job: %m");
-                        goto finish;
-                }
-
-                i->tags_job->on_finished = dkr_pull_job_on_finished_v2;
-                i->tags_job->on_progress = dkr_pull_job_on_progress;
-
-                r = pull_job_begin(i->tags_job);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to start tags job: %m");
-                        goto finish;
-                }
-
-        } else if (i->tags_job == j) {
-                const char *url;
-                _cleanup_free_ char *buf;
-                _cleanup_json_variant_unref_ JsonVariant *doc = NULL;
-                JsonVariant *e = NULL;
-
-                assert(!i->ancestry_job);
-                assert(!i->json_job);
-                assert(!i->layer_job);
-
-                buf = strndup((const char *)j->payload, j->payload_size);
-                if (!buf) {
-                        r = -ENOMEM;
-                        log_oom();
-                        goto finish;
-                }
-
-                r = json_parse(buf, &doc);
-                if (r < 0) {
-                        log_error("Unable to parse bearer token\n%s", j->payload);
-                        goto finish;
-                }
-
-                e = json_variant_value(doc, "token");
-                if (!e || e->type != JSON_VARIANT_STRING) {
-                        r = -EBADMSG;
-                        log_error("Invalid JSON format for Bearer token");
-                        goto finish;
-                }
-
-                r = free_and_strdup(&i->response_token, json_variant_string(e));
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-
-                url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/manifests/", i->reference);
-                r = pull_job_new(&i->ancestry_job, url, i->glue, i);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to allocate ancestry job: %m");
-                        goto finish;
-                }
-
-                r = dkr_pull_add_bearer_token(i, i->ancestry_job);
-                if (r < 0)
-                        goto finish;
-
-                i->ancestry_job->on_finished = dkr_pull_job_on_finished_v2;
-                i->ancestry_job->on_progress = dkr_pull_job_on_progress;
-                i->ancestry_job->on_header = dkr_pull_job_on_header;
-
-
-                r = pull_job_begin(i->ancestry_job);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to start ancestry job: %m");
-                        goto finish;
-                }
-
-        } else if (i->ancestry_job == j) {
-
-                _cleanup_json_variant_unref_ JsonVariant *doc = NULL, *compat = NULL;
-                JsonVariant *e = NULL;
-                _cleanup_strv_free_ char **ancestry = NULL;
-                size_t allocated = 0, size = 0;
-                char *path = NULL, **k = NULL;
-
-                r = json_parse((const char *)j->payload, &doc);
-                if (r < 0) {
-                        log_error("Invalid JSON Manifest");
-                        goto finish;
-                }
-
-                e = json_variant_value(doc, "fsLayers");
-                if (!e || e->type != JSON_VARIANT_ARRAY || e->size == 0) {
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                log_info("JSON manifest with schema v%"PRIi64" for %s parsed!",
-                                json_variant_integer(json_variant_value(doc, "schemaVersion")),
-                                json_variant_string(json_variant_value(doc, "name")));
-
-                for (unsigned z = 0; z < e->size; z++) {
-                        JsonVariant *f = json_variant_element(e, z), *g = NULL;
-                        const char *layer;
-                        if (f->type != JSON_VARIANT_OBJECT) {
-                                r = -EBADMSG;
-                                goto finish;
-                        }
-
-                        g = json_variant_value(f, "blobSum");
-
-                        layer = json_variant_string(g);
-                        if (!dkr_digest_is_valid(layer)) {
-                                r = -EBADMSG;
-                                goto finish;
-                        }
-
-                        if (!GREEDY_REALLOC(ancestry, allocated, size + 2)) {
-                                r = -ENOMEM;
-                                log_oom();
-                                goto finish;
-                        }
-
-                        ancestry[size] = strdup(layer);
-                        if (!ancestry[size]) {
-                                r = -ENOMEM;
-                                log_oom();
-                                goto finish;
-                        }
-
-                        ancestry[size+1] = NULL;
-                        size += 1;
-                }
-
-                e = json_variant_value(doc, "history");
-                if (!e || e->type != JSON_VARIANT_ARRAY) {
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                e = json_variant_element(e, 0);
-                e = json_variant_value(e, "v1Compatibility");
-                r = json_parse(json_variant_string(e), &compat);
-                if (r < 0) {
-                        log_error("Invalid v1Compatibility JSON");
-                        goto finish;
-                }
-
-                e = json_variant_value(compat, "id");
-
-                strv_free(i->ancestry);
-                i->ancestry = strv_reverse(strv_uniq(ancestry));
-                i->n_ancestry = strv_length(i->ancestry);
-                i->current_ancestry = 0;
-                i->id = strdup(i->ancestry[i->n_ancestry - 1]);
-                if (!i->id) {
-                        r = -ENOMEM;
-                        log_oom();
-                        goto finish;
-                }
-                path = strjoin(i->image_root, "/.dkr-", json_variant_string(e), NULL);
-                if (!path) {
-                        r = -ENOMEM;
-                        log_oom();
-                        goto finish;
-                }
-                free(i->image_root);
-                i->image_root = path;
-                ancestry = NULL;
-
-                log_info("Required layers:\n");
-                STRV_FOREACH(k, i->ancestry)
-                        log_info("\t%s", *k);
-                log_info("\nProvenance:\n\tImageID: %s\n\tDigest:  %s", json_variant_string(e), i->response_digest);
-
-                dkr_pull_report_progress(i, DKR_DOWNLOADING);
-
-                r = dkr_pull_pull_layer_v2(i);
-                if (r < 0)
-                        goto finish;
-
-        } else if (i->layer_job == j) {
-                assert(i->temp_path);
-                assert(i->final_path);
-
-                j->disk_fd = safe_close(j->disk_fd);
-
-                if (i->tar_pid > 0) {
-                        r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
-                        i->tar_pid = 0;
-                        if (r < 0)
-                                goto finish;
-                }
-
-                r = aufs_resolve(i->temp_path);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
-                        goto finish;
-                }
-
-                r = btrfs_subvol_set_read_only(i->temp_path, true);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to mark snapshot read-only: %m");
-                        goto finish;
-                }
-
-                if (rename(i->temp_path, i->final_path) < 0) {
-                        log_error_errno(errno, "Failed to rename snaphsot: %m");
-                        goto finish;
-                }
-
-                log_info("Completed writing to layer %s.", i->final_path);
-
-                i->layer_job = pull_job_unref(i->layer_job);
-                free(i->temp_path);
-                i->temp_path = NULL;
-                free(i->final_path);
-                i->final_path = NULL;
-
-                i->current_ancestry ++;
-                r = dkr_pull_pull_layer_v2(i);
-                if (r < 0)
-                        goto finish;
-
-        } else if (i->json_job != j)
-                assert_not_reached("Got finished event for unknown curl object");
-
-        if (!dkr_pull_is_done(i))
-                return;
-
-        dkr_pull_report_progress(i, DKR_COPYING);
-
-        r = dkr_pull_make_local_copy(i, DKR_PULL_V2);
-        if (r < 0)
-                goto finish;
-
-        r = 0;
-
-finish:
-        if (i->on_finished)
-                i->on_finished(i, r, i->userdata);
-        else
-                sd_event_exit(i->event, r);
-
-}
-
-static void dkr_pull_job_on_finished(PullJob *j) {
-        DkrPull *i;
-        int r;
-
-        assert(j);
-        assert(j->userdata);
-
-        i = j->userdata;
-        if (j->error != 0) {
-                if (j == i->images_job)
-                        log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
-                else if (j == i->tags_job)
-                        log_error_errno(j->error, "Failed to retrieve tags list.");
-                else if (j == i->ancestry_job)
-                        log_error_errno(j->error, "Failed to retrieve ancestry list.");
-                else if (j == i->json_job)
-                        log_error_errno(j->error, "Failed to retrieve json data.");
-                else
-                        log_error_errno(j->error, "Failed to retrieve layer data.");
-
-                r = j->error;
-                goto finish;
-        }
-
-        if (i->images_job == j) {
-                const char *url;
-
-                assert(!i->tags_job);
-                assert(!i->ancestry_job);
-                assert(!i->json_job);
-                assert(!i->layer_job);
-
-                if (strv_isempty(i->response_registries)) {
-                        r = -EBADMSG;
-                        log_error("Didn't get registry information.");
-                        goto finish;
-                }
-
-                log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
-                dkr_pull_report_progress(i, DKR_RESOLVING);
-
-                url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->reference);
-                r = pull_job_new(&i->tags_job, url, i->glue, i);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to allocate tags job: %m");
-                        goto finish;
-                }
-
-                r = dkr_pull_add_token(i, i->tags_job);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-
-                i->tags_job->on_finished = dkr_pull_job_on_finished;
-                i->tags_job->on_progress = dkr_pull_job_on_progress;
-
-                r = pull_job_begin(i->tags_job);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to start tags job: %m");
-                        goto finish;
-                }
-
-        } else if (i->tags_job == j) {
-                const char *url;
-                char *id = NULL;
-
-                assert(!i->ancestry_job);
-                assert(!i->json_job);
-                assert(!i->layer_job);
-
-                r = parse_id(j->payload, j->payload_size, &id);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse JSON id.");
-                        goto finish;
-                }
-
-                free(i->id);
-                i->id = id;
-
-                log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
-                dkr_pull_report_progress(i, DKR_METADATA);
-
-                url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
-                r = pull_job_new(&i->ancestry_job, url, i->glue, i);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to allocate ancestry job: %m");
-                        goto finish;
-                }
-
-                r = dkr_pull_add_token(i, i->ancestry_job);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-
-                i->ancestry_job->on_finished = dkr_pull_job_on_finished;
-                i->ancestry_job->on_progress = dkr_pull_job_on_progress;
-
-                url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
-                r = pull_job_new(&i->json_job, url, i->glue, i);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to allocate json job: %m");
-                        goto finish;
-                }
-
-                r = dkr_pull_add_token(i, i->json_job);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-
-                i->json_job->on_finished = dkr_pull_job_on_finished;
-                i->json_job->on_progress = dkr_pull_job_on_progress;
-
-                r = pull_job_begin(i->ancestry_job);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to start ancestry job: %m");
-                        goto finish;
-                }
-
-                r = pull_job_begin(i->json_job);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to start json job: %m");
-                        goto finish;
-                }
-
-        } else if (i->ancestry_job == j) {
-                char **ancestry = NULL, **k;
-                unsigned n;
-
-                assert(!i->layer_job);
-
-                r = parse_ancestry(j->payload, j->payload_size, &ancestry);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse JSON id.");
-                        goto finish;
-                }
-
-                n = strv_length(ancestry);
-                if (n <= 0 || !streq(ancestry[n-1], i->id)) {
-                        log_error("Ancestry doesn't end in main layer.");
-                        strv_free(ancestry);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                log_info("Ancestor lookup succeeded, requires layers:\n");
-                STRV_FOREACH(k, ancestry)
-                        log_info("\t%s", *k);
-
-                strv_free(i->ancestry);
-                i->ancestry = ancestry;
-                i->n_ancestry = n;
-                i->current_ancestry = 0;
-
-                dkr_pull_report_progress(i, DKR_DOWNLOADING);
-
-                r = dkr_pull_pull_layer(i);
-                if (r < 0)
-                        goto finish;
-
-        } else if (i->layer_job == j) {
-                assert(i->temp_path);
-                assert(i->final_path);
-
-                j->disk_fd = safe_close(j->disk_fd);
-
-                if (i->tar_pid > 0) {
-                        r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
-                        i->tar_pid = 0;
-                        if (r < 0)
-                                goto finish;
-                }
-
-                r = aufs_resolve(i->temp_path);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
-                        goto finish;
-                }
-
-                r = btrfs_subvol_set_read_only(i->temp_path, true);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to mark snapshot read-only: %m");
-                        goto finish;
-                }
-
-                if (rename(i->temp_path, i->final_path) < 0) {
-                        log_error_errno(errno, "Failed to rename snaphsot: %m");
-                        goto finish;
-                }
-
-                log_info("Completed writing to layer %s.", i->final_path);
-
-                i->layer_job = pull_job_unref(i->layer_job);
-                i->temp_path = mfree(i->temp_path);
-                i->final_path = mfree(i->final_path);
-
-                i->current_ancestry ++;
-                r = dkr_pull_pull_layer(i);
-                if (r < 0)
-                        goto finish;
-
-        } else if (i->json_job != j)
-                assert_not_reached("Got finished event for unknown curl object");
-
-        if (!dkr_pull_is_done(i))
-                return;
-
-        dkr_pull_report_progress(i, DKR_COPYING);
-
-        r = dkr_pull_make_local_copy(i, DKR_PULL_V1);
-        if (r < 0)
-                goto finish;
-
-        r = 0;
-finish:
-        if (i->on_finished)
-                i->on_finished(i, r, i->userdata);
-        else
-                sd_event_exit(i->event, r);
-}
-
-static int get_protocol_address(char **protocol, char **address, const char *url) {
-        const char *sep, *dot;
-        _cleanup_free_ char *a = NULL, *p = NULL;
-
-        sep = strstr(url, "://");
-        if (!sep)
-                return -EINVAL;
-
-        dot = strrchr(url, '.');
-        if (!dot)
-                return -EINVAL;
-        dot--;
-
-        p = strndup(url, (sep - url) + 3);
-        if (!p)
-                return log_oom();
-
-        while (dot > (sep + 3) && *dot != '.')
-                dot--;
-
-        a = strdup(dot + 1);
-        if (!a)
-                return log_oom();
-
-        *address = a;
-        *protocol = p;
-        a = p = NULL;
-
-        return 0;
-}
-
-int dkr_pull_start(DkrPull *i, const char *name, const char *reference, const char *local, bool force_local, DkrPullVersion version) {
-        const char *url;
-        int r;
-
-        assert(i);
-
-        if (!dkr_name_is_valid(name))
-                return -EINVAL;
-
-        if (reference && !dkr_ref_is_valid(reference))
-                return -EINVAL;
-
-        if (local && !machine_name_is_valid(local))
-                return -EINVAL;
-
-        if (i->images_job)
-                return -EBUSY;
-
-        if (!reference)
-                reference = "latest";
-
-        free(i->index_protocol);
-        free(i->index_address);
-        r = get_protocol_address(&i->index_protocol, &i->index_address, i->index_url);
-        if (r < 0)
-                return r;
-
-        r = free_and_strdup(&i->local, local);
-        if (r < 0)
-                return r;
-        i->force_local = force_local;
-
-        r = free_and_strdup(&i->name, name);
-        if (r < 0)
-                return r;
-        r = free_and_strdup(&i->reference, reference);
-        if (r < 0)
-                return r;
-
-        url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
-
-        r = pull_job_new(&i->images_job, url, i->glue, i);
-        if (r < 0)
-                return r;
-
-        r = dkr_pull_add_token(i, i->images_job);
-        if (r < 0)
-                return r;
-
-        if (version == DKR_PULL_V1)
-                i->images_job->on_finished = dkr_pull_job_on_finished;
-        else
-                i->images_job->on_finished = dkr_pull_job_on_finished_v2;
-
-        i->images_job->on_header = dkr_pull_job_on_header;
-        i->images_job->on_progress = dkr_pull_job_on_progress;
-
-        return pull_job_begin(i->images_job);
-}
diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h
deleted file mode 100644 (file)
index a95d912..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2014 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#pragma once
-
-#include "sd-event.h"
-
-#include "util.h"
-
-typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion;
-typedef struct DkrPull DkrPull;
-
-typedef void (*DkrPullFinished)(DkrPull *pull, int error, void *userdata);
-
-int dkr_pull_new(DkrPull **pull, sd_event *event, const char *index_url, const char *image_root, DkrPullFinished on_finished, void *userdata);
-DkrPull* dkr_pull_unref(DkrPull *pull);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DkrPull*, dkr_pull_unref);
-
-int dkr_pull_start(DkrPull *pull, const char *name, const char *tag, const char *local, bool force_local, DkrPullVersion version);
index fc93228a0bf76886e6583d2dbfc25ce98de9cfb8..e0631bdeaf8543d40510806e93e280b6dda8f583 100644 (file)
@@ -28,7 +28,6 @@
 #include "import-util.h"
 #include "machine-image.h"
 #include "parse-util.h"
-#include "pull-dkr.h"
 #include "pull-raw.h"
 #include "pull-tar.h"
 #include "signal-util.h"
@@ -39,7 +38,6 @@
 static bool arg_force = false;
 static const char *arg_image_root = "/var/lib/machines";
 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
-static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
 static bool arg_settings = true;
 
 static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -220,114 +218,6 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
         return -r;
 }
 
-static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
-        sd_event *event = userdata;
-        assert(pull);
-
-        if (error == 0)
-                log_info("Operation completed successfully.");
-
-        sd_event_exit(event, abs(error));
-}
-
-static int pull_dkr(int argc, char *argv[], void *userdata) {
-        _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
-        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-        const char *name, *reference, *local, *digest;
-        int r;
-
-        if (!arg_dkr_index_url) {
-                log_error("Please specify an index URL with --dkr-index-url=");
-                return -EINVAL;
-        }
-
-        if (arg_verify != IMPORT_VERIFY_NO) {
-                log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
-                return -EINVAL;
-        }
-
-        digest = strchr(argv[1], '@');
-        if (digest) {
-                reference = digest + 1;
-                name = strndupa(argv[1], digest - argv[1]);
-        } else {
-                reference = strchr(argv[1], ':');
-                if (reference) {
-                        name = strndupa(argv[1], reference - argv[1]);
-                        reference++;
-                } else {
-                        name = argv[1];
-                        reference = "latest";
-                }
-        }
-
-        if (!dkr_name_is_valid(name)) {
-                log_error("Remote name '%s' is not valid.", name);
-                return -EINVAL;
-        }
-
-        if (!dkr_ref_is_valid(reference)) {
-                log_error("Tag name '%s' is not valid.", reference);
-                return -EINVAL;
-        }
-
-        if (argc >= 3)
-                local = argv[2];
-        else {
-                local = strchr(name, '/');
-                if (local)
-                        local++;
-                else
-                        local = name;
-        }
-
-        if (isempty(local) || streq(local, "-"))
-                local = NULL;
-
-        if (local) {
-                if (!machine_name_is_valid(local)) {
-                        log_error("Local image name '%s' is not valid.", local);
-                        return -EINVAL;
-                }
-
-                if (!arg_force) {
-                        r = image_find(local, NULL);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        else if (r > 0) {
-                                log_error_errno(EEXIST, "Image '%s' already exists.", local);
-                                return -EEXIST;
-                        }
-                }
-
-                log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
-        } else
-                log_info("Pulling '%s' with reference '%s'.", name, reference);
-
-        r = sd_event_default(&event);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate event loop: %m");
-
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
-        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
-
-        r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate puller: %m");
-
-        r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
-        if (r < 0)
-                return log_error_errno(r, "Failed to pull image: %m");
-
-        r = sd_event_loop(event);
-        if (r < 0)
-                return log_error_errno(r, "Failed to run event loop: %m");
-
-        log_info("Exiting.");
-        return -r;
-}
-
 static int help(int argc, char *argv[], void *userdata) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -338,12 +228,10 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --verify=MODE            Verify downloaded image, one of: 'no',\n"
                "                              'checksum', 'signature'\n"
                "     --settings=BOOL          Download settings file with image\n"
-               "     --image-root=PATH        Image root directory\n"
-               "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
+               "     --image-root=PATH        Image root directory\n\n"
                "Commands:\n"
                "  tar URL [NAME]              Download a TAR image\n"
-               "  raw URL [NAME]              Download a RAW image\n"
-               "  dkr REMOTE [NAME]           Download a DKR image\n",
+               "  raw URL [NAME]              Download a RAW image\n",
                program_invocation_short_name);
 
         return 0;
@@ -354,7 +242,6 @@ static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_FORCE,
-                ARG_DKR_INDEX_URL,
                 ARG_IMAGE_ROOT,
                 ARG_VERIFY,
                 ARG_SETTINGS,
@@ -364,7 +251,6 @@ static int parse_argv(int argc, char *argv[]) {
                 { "help",            no_argument,       NULL, 'h'                 },
                 { "version",         no_argument,       NULL, ARG_VERSION         },
                 { "force",           no_argument,       NULL, ARG_FORCE           },
-                { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
                 { "verify",          required_argument, NULL, ARG_VERIFY          },
                 { "settings",        required_argument, NULL, ARG_SETTINGS        },
@@ -390,15 +276,6 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_force = true;
                         break;
 
-                case ARG_DKR_INDEX_URL:
-                        if (!http_url_is_valid(optarg)) {
-                                log_error("Index URL is not valid: %s", optarg);
-                                return -EINVAL;
-                        }
-
-                        arg_dkr_index_url = optarg;
-                        break;
-
                 case ARG_IMAGE_ROOT:
                         arg_image_root = optarg;
                         break;
@@ -436,7 +313,6 @@ static int pull_main(int argc, char *argv[]) {
                 { "help", VERB_ANY, VERB_ANY, 0, help     },
                 { "tar",  2,        3,        0, pull_tar },
                 { "raw",  2,        3,        0, pull_raw },
-                { "dkr",  2,        3,        0, pull_dkr },
                 {}
         };
 
index 006791a542186e0e8af7cc1b53a6c85df40e5aca..4e96fb0a4d1d0f8dd9ede2736f86ae995cfe7914 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;
 
index b2f5fbf6b4caddaad2e0182893807ff563bf4594..3f93e8523264c520d04eb6202f3c0ab2fcfafb6b 100644 (file)
@@ -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 1a3d2cdd800d59299faeafa223ba0169a3c2fd23..18281658942c3d0cc5a05a50d959a496be02c092 100644 (file)
@@ -58,7 +58,8 @@ static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
 
-int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+int compress_blob_xz(const void *src, uint64_t src_size,
+                     void *dst, size_t dst_alloc_size, size_t *dst_size) {
 #ifdef HAVE_XZ
         static const lzma_options_lzma opt = {
                 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
@@ -74,6 +75,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
         assert(src);
         assert(src_size > 0);
         assert(dst);
+        assert(dst_alloc_size > 0);
         assert(dst_size);
 
         /* Returns < 0 if we couldn't compress the data or the
@@ -83,7 +85,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
                 return -ENOBUFS;
 
         ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
-                                        src, src_size, dst, &out_pos, src_size - 1);
+                                        src, src_size, dst, &out_pos, dst_alloc_size);
         if (ret != LZMA_OK)
                 return -ENOBUFS;
 
@@ -94,13 +96,15 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
 #endif
 }
 
-int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+int compress_blob_lz4(const void *src, uint64_t src_size,
+                      void *dst, size_t dst_alloc_size, size_t *dst_size) {
 #ifdef HAVE_LZ4
         int r;
 
         assert(src);
         assert(src_size > 0);
         assert(dst);
+        assert(dst_alloc_size > 0);
         assert(dst_size);
 
         /* Returns < 0 if we couldn't compress the data or the
@@ -109,7 +113,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst
         if (src_size < 9)
                 return -ENOBUFS;
 
-        r = LZ4_compress_limitedOutput(src, dst + 8, src_size, src_size - 8 - 1);
+        r = LZ4_compress_limitedOutput(src, dst + 8, src_size, (int) dst_alloc_size - 8);
         if (r <= 0)
                 return -ENOBUFS;
 
@@ -306,6 +310,7 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
          * prefix */
 
         int r;
+        size_t size;
 
         assert(src);
         assert(src_size > 0);
@@ -322,10 +327,18 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
 
         r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8,
                                         prefix_len + 1, *buffer_size);
+        if (r >= 0)
+                size = (unsigned) r;
+        else {
+                /* lz4 always tries to decode full "sequence", so in
+                 * pathological cases might need to decompress the
+                 * full field. */
+                r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
+                if (r < 0)
+                        return r;
+        }
 
-        if (r < 0)
-                return -EBADMSG;
-        if ((unsigned) r >= prefix_len + 1)
+        if (size >= prefix_len + 1)
                 return memcmp(*buffer, prefix, prefix_len) == 0 &&
                         ((const uint8_t*) *buffer)[prefix_len] == extra;
         else
@@ -438,7 +451,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
         _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
         _cleanup_free_ char *buf = NULL;
         char *src = NULL;
-        size_t size, n, total_in = 0, total_out = 0, offset = 0, frame_size;
+        size_t size, n, total_in = 0, total_out, offset = 0, frame_size;
         struct stat st;
         int r;
         static const LZ4F_compressOptions_t options = {
@@ -461,7 +474,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
         if (!buf)
                 return -ENOMEM;
 
-        n = offset = LZ4F_compressBegin(ctx, buf, size, &preferences);
+        n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
         if (LZ4F_isError(n))
                 return -EINVAL;
 
index 9a065eb763baca54721e9950bb72260518d72a30..758598730a35775d44c4790d807455278c5666f0 100644 (file)
 const char* object_compressed_to_string(int compression);
 int object_compressed_from_string(const char *compression);
 
-int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
-int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
+int compress_blob_xz(const void *src, uint64_t src_size,
+                     void *dst, size_t dst_alloc_size, size_t *dst_size);
+int compress_blob_lz4(const void *src, uint64_t src_size,
+                      void *dst, size_t dst_alloc_size, size_t *dst_size);
 
-static inline int compress_blob(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
+static inline int compress_blob(const void *src, uint64_t src_size,
+                                void *dst, size_t dst_alloc_size, size_t *dst_size) {
         int r;
 #ifdef HAVE_LZ4
-        r = compress_blob_lz4(src, src_size, dst, dst_size);
+        r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
         if (r == 0)
                 return OBJECT_COMPRESSED_LZ4;
 #else
-        r = compress_blob_xz(src, src_size, dst, dst_size);
+        r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
         if (r == 0)
                 return OBJECT_COMPRESSED_XZ;
 #endif
index f9ff9545dddd7493e500ff32b2060ff53bfa3a7a..9e362bacae56c0a3b00e4633fb296fd0c6681986 100644 (file)
@@ -169,8 +169,7 @@ JournalFile* journal_file_close(JournalFile *f) {
         safe_close(f->fd);
         free(f->path);
 
-        if (f->mmap)
-                mmap_cache_unref(f->mmap);
+        mmap_cache_unref(f->mmap);
 
         ordered_hashmap_free_free(f->chain_cache);
 
@@ -1085,7 +1084,7 @@ static int journal_file_append_data(
         if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) {
                 size_t rsize = 0;
 
-                compression = compress_blob(data, size, o->data.payload, &rsize);
+                compression = compress_blob(data, size, o->data.payload, size - 1, &rsize);
 
                 if (compression >= 0) {
                         o->object.size = htole64(offsetof(Object, data.payload) + rsize);
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 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 5a07ddda764552df5f4275ec273d71b5e1d4a858..eb4b092e80ac08a2dbb74c683088373d4a408ba3 100644 (file)
@@ -348,7 +348,10 @@ static void mmap_cache_free(MMapCache *m) {
 }
 
 MMapCache* mmap_cache_unref(MMapCache *m) {
-        assert(m);
+
+        if (!m)
+                return NULL;
+
         assert(m->n_ref > 0);
 
         m->n_ref --;
index 5cde7f17f7d34dad34879a607cf941241cfc7aa1..cd5160154ad4887b0290d56b8a35d43b96eab39d 100644 (file)
@@ -1940,10 +1940,14 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
                 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
                 if (compression) {
 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
-                        if (decompress_startswith(compression,
+                        r = decompress_startswith(compression,
                                                   o->data.payload, l,
                                                   &f->compress_buffer, &f->compress_buffer_size,
-                                                  field, field_length, '=')) {
+                                                  field, field_length, '=');
+                        if (r < 0)
+                                log_debug_errno(r, "Cannot decompress %s object of length %zu at offset "OFSfmt": %m",
+                                                object_compressed_to_string(compression), l, p);
+                        else if (r > 0) {
 
                                 size_t rsize;
 
index 93ea9c6318fb4437b7ee7f2a2fa17d492cbe64c8..baed0d82a46befa2ace2cb51a8289d46902fc4f5 100644 (file)
@@ -27,7 +27,8 @@
 #include "string-util.h"
 #include "util.h"
 
-typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, size_t *dst_size);
+typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
+                         size_t dst_alloc_size, size_t *dst_size);
 typedef int (decompress_t)(const void *src, uint64_t src_size,
                            void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
 
@@ -111,8 +112,8 @@ static void test_compress_decompress(const char* label, const char* type,
 
                 memzero(buf, MIN(size + 1000, MAX_SIZE));
 
-                r = compress(text, size, buf, &j);
-                /* assume compression must be successful except for small inputs */
+                r = compress(text, size, buf, size, &j);
+                /* assume compression must be successful except for small or random inputs */
                 assert_se(r == 0 || (size < 2048 && r == -ENOBUFS) || streq(type, "random"));
 
                 /* check for overwrites */
index b9d90a89887f976d8cf685600187ca060bd4615d..68c9a4d76c0a76a289a29ffd1f622b5b7b2187e7 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#ifdef HAVE_LZ4
+#include <lz4.h>
+#endif
+
 #include "alloc-util.h"
 #include "compress.h"
 #include "fd-util.h"
@@ -38,7 +42,7 @@
 #endif
 
 typedef int (compress_blob_t)(const void *src, uint64_t src_size,
-                              void *dst, size_t *dst_size);
+                              void *dst, size_t dst_alloc_size, size_t *dst_size);
 typedef int (decompress_blob_t)(const void *src, uint64_t src_size,
                                 void **dst, size_t *dst_alloc_size,
                                 size_t* dst_size, size_t dst_max);
@@ -57,15 +61,14 @@ static void test_compress_decompress(int compression,
                                      size_t data_len,
                                      bool may_fail) {
         char compressed[512];
-        size_t csize = 512;
-        size_t usize = 0;
+        size_t csize, usize = 0;
         _cleanup_free_ char *decompressed = NULL;
         int r;
 
         log_info("/* testing %s %s blob compression/decompression */",
                  object_compressed_to_string(compression), data);
 
-        r = compress(data, data_len, compressed, &csize);
+        r = compress(data, data_len, compressed, sizeof(compressed), &csize);
         if (r == -ENOBUFS) {
                 log_info_errno(r, "compression failed: %m");
                 assert_se(may_fail);
@@ -101,43 +104,45 @@ static void test_decompress_startswith(int compression,
                                        size_t data_len,
                                        bool may_fail) {
 
-        char compressed[512];
-        size_t csize = 512;
-        size_t usize = 0;
-        _cleanup_free_ char *decompressed = NULL;
+        char *compressed;
+        _cleanup_free_ char *compressed1 = NULL, *compressed2 = NULL, *decompressed = NULL;
+        size_t csize, usize = 0, len;
         int r;
 
-        log_info("/* testing decompress_startswith with %s on %s text*/",
+        log_info("/* testing decompress_startswith with %s on %.20s text*/",
                  object_compressed_to_string(compression), data);
 
-        r = compress(data, data_len, compressed, &csize);
+#define BUFSIZE_1 512
+#define BUFSIZE_2 20000
+
+        compressed = compressed1 = malloc(BUFSIZE_1);
+        assert_se(compressed1);
+        r = compress(data, data_len, compressed, BUFSIZE_1, &csize);
         if (r == -ENOBUFS) {
                 log_info_errno(r, "compression failed: %m");
                 assert_se(may_fail);
-                return;
+
+                compressed = compressed2 = malloc(BUFSIZE_2);
+                assert_se(compressed2);
+                r = compress(data, data_len, compressed, BUFSIZE_2, &csize);
+                assert(r == 0);
         }
         assert_se(r == 0);
 
-        assert_se(decompress_sw(compressed,
-                                csize,
-                                (void **) &decompressed,
-                                &usize,
-                                data, strlen(data), '\0') > 0);
-        assert_se(decompress_sw(compressed,
-                                csize,
-                                (void **) &decompressed,
-                                &usize,
-                                data, strlen(data), 'w') == 0);
-        assert_se(decompress_sw(compressed,
-                                csize,
-                                (void **) &decompressed,
-                                &usize,
-                                "barbarbar", 9, ' ') == 0);
-        assert_se(decompress_sw(compressed,
-                                csize,
-                                (void **) &decompressed,
-                                &usize,
-                                data, strlen(data), '\0') > 0);
+        len = strlen(data);
+
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
+        assert_se(r > 0);
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, 'w');
+        assert_se(r == 0);
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, "barbarbar", 9, ' ');
+        assert_se(r == 0);
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, data[len-1]);
+        assert_se(r > 0);
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, 'w');
+        assert_se(r == 0);
+        r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
+        assert_se(r > 0);
 }
 
 static void test_compress_stream(int compression,
@@ -199,6 +204,44 @@ static void test_compress_stream(int compression,
         assert_se(unlink(pattern2) == 0);
 }
 
+#ifdef HAVE_LZ4
+static void test_lz4_decompress_partial(void) {
+        char buf[20000];
+        size_t buf_size = sizeof(buf), compressed;
+        int r;
+        _cleanup_free_ char *huge = NULL;
+
+#define HUGE_SIZE (4096*1024)
+        huge = malloc(HUGE_SIZE);
+        memset(huge, 'x', HUGE_SIZE);
+        memcpy(huge, "HUGE=", 5);
+
+        r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size);
+        assert_se(r >= 0);
+        compressed = r;
+        log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
+
+        r = LZ4_decompress_safe(buf, huge, r, HUGE_SIZE);
+        assert_se(r >= 0);
+        log_info("Decompressed → %i", r);
+
+        r = LZ4_decompress_safe_partial(buf, huge,
+                                        compressed,
+                                        12, HUGE_SIZE);
+        assert_se(r >= 0);
+        log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
+
+        /* We expect this to fail, because that's how current lz4 works. If this
+         * call succeeds, then lz4 has been fixed, and we need to change our code.
+         */
+        r = LZ4_decompress_safe_partial(buf, huge,
+                                        compressed,
+                                        12, HUGE_SIZE-1);
+        assert_se(r < 0);
+        log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r);
+}
+#endif
+
 int main(int argc, char *argv[]) {
         const char text[] =
                 "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
@@ -206,6 +249,11 @@ int main(int argc, char *argv[]) {
 
         char data[512] = "random\0";
 
+        char huge[4096*1024];
+        memset(huge, 'x', sizeof(huge));
+        memcpy(huge, "HUGE=", 5);
+        char_array_0(huge);
+
         log_set_max_level(LOG_DEBUG);
 
         random_bytes(data + 7, sizeof(data) - 7);
@@ -215,12 +263,17 @@ int main(int argc, char *argv[]) {
                                  text, sizeof(text), false);
         test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
                                  data, sizeof(data), true);
+
         test_decompress_startswith(OBJECT_COMPRESSED_XZ,
                                    compress_blob_xz, decompress_startswith_xz,
                                    text, sizeof(text), false);
         test_decompress_startswith(OBJECT_COMPRESSED_XZ,
                                    compress_blob_xz, decompress_startswith_xz,
                                    data, sizeof(data), true);
+        test_decompress_startswith(OBJECT_COMPRESSED_XZ,
+                                   compress_blob_xz, decompress_startswith_xz,
+                                   huge, sizeof(huge), true);
+
         test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
                              compress_stream_xz, decompress_stream_xz, argv[0]);
 #else
@@ -232,15 +285,21 @@ int main(int argc, char *argv[]) {
                                  text, sizeof(text), false);
         test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
                                  data, sizeof(data), true);
+
         test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
                                    compress_blob_lz4, decompress_startswith_lz4,
                                    text, sizeof(text), false);
         test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
                                    compress_blob_lz4, decompress_startswith_lz4,
                                    data, sizeof(data), true);
+        test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
+                                   compress_blob_lz4, decompress_startswith_lz4,
+                                   huge, sizeof(huge), true);
 
         test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
                              compress_stream_lz4, decompress_stream_lz4, argv[0]);
+
+        test_lz4_decompress_partial();
 #else
         log_info("/* LZ4 test skipped */");
 #endif
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 8d486fcbbdd62a3297df74f9490bbe8ba4366c9e..6e2594d0011daae607907127818f0a3f8cb35f22 100644 (file)
@@ -67,12 +67,14 @@ 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_NO_SUCH_TRANSFER,             ENXIO),
         SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS,         EBUSY),
index f2092795f410b40c3142338bbc8d2495980040b2..7a5f6cda87862a2ba7cfdffd1de2ac0939090511 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_DNS "org.freedesktop.resolve1.DnsError."
 
 #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
index 6c05444e9a057b172c4395a9fbbbc7f76b562630..b2d685855ee2e4a021f389bad03d30df5c91e14d 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"
@@ -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 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 3191b458d15e1bfc9d9112c91ab7ef90a2e66099..aeb06ad9a8a52ffda61db0ce773fdf8c5578d673 100644 (file)
@@ -37,6 +37,7 @@
 #include "process-util.h"
 #include "set.h"
 #include "signal-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "time-util.h"
 #include "util.h"
@@ -60,6 +61,23 @@ typedef enum EventSourceType {
         _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
 } EventSourceType;
 
+static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
+        [SOURCE_IO] = "io",
+        [SOURCE_TIME_REALTIME] = "realtime",
+        [SOURCE_TIME_BOOTTIME] = "bootime",
+        [SOURCE_TIME_MONOTONIC] = "monotonic",
+        [SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
+        [SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
+        [SOURCE_SIGNAL] = "signal",
+        [SOURCE_CHILD] = "child",
+        [SOURCE_DEFER] = "defer",
+        [SOURCE_POST] = "post",
+        [SOURCE_EXIT] = "exit",
+        [SOURCE_WATCHDOG] = "watchdog",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
+
 /* All objects we use in epoll events start with this value, so that
  * we know how to dispatch it */
 typedef enum WakeupType {
@@ -207,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;
 
@@ -218,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);
@@ -426,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;
 
@@ -482,7 +509,8 @@ static void source_io_unregister(sd_event_source *s) {
 
         r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
         if (r < 0)
-                log_debug_errno(errno, "Failed to remove source %s from epoll: %m", strna(s->description));
+                log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m",
+                                strna(s->description), event_source_type_to_string(s->type));
 
         s->io.registered = false;
 }
@@ -2281,12 +2309,9 @@ static int source_dispatch(sd_event_source *s) {
 
         s->dispatching = false;
 
-        if (r < 0) {
-                if (s->description)
-                        log_debug_errno(r, "Event source '%s' returned error, disabling: %m", s->description);
-                else
-                        log_debug_errno(r, "Event source %p returned error, disabling: %m", s);
-        }
+        if (r < 0)
+                log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m",
+                                strna(s->description), event_source_type_to_string(s->type));
 
         if (s->n_ref == 0)
                 source_free(s);
@@ -2319,12 +2344,9 @@ static int event_prepare(sd_event *e) {
                 r = s->prepare(s, s->userdata);
                 s->dispatching = false;
 
-                if (r < 0) {
-                        if (s->description)
-                                log_debug_errno(r, "Prepare callback of event source '%s' returned error, disabling: %m", s->description);
-                        else
-                                log_debug_errno(r, "Prepare callback of event source %p returned error, disabling: %m", s);
-                }
+                if (r < 0)
+                        log_debug_errno(r, "Prepare callback of event source %s (type %s) returned error, disabling: %m",
+                                        strna(s->description), event_source_type_to_string(s->type));
 
                 if (s->n_ref == 0)
                         source_free(s);
@@ -2609,6 +2631,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;
 
@@ -2617,11 +2651,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);
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 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 c1643cf41a523cfa66ebaa6ba84d934f18ec4a67..4631f5fc90301dcbe04b7be89fcd8fb21de51107 100644 (file)
@@ -1944,9 +1944,9 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
                 action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
                 action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
         } else if (streq(type, "poweroff")) {
-                action = "org.freedesktop.login1.poweroff";
-                action_multiple_sessions = "org.freedesktop.login1.poweroff-multiple-sessions";
-                action_ignore_inhibit = "org.freedesktop.login1.poweroff-ignore-inhibit";
+                action = "org.freedesktop.login1.power-off";
+                action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
+                action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
         } else
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
 
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 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 0a01bd3e203f737f0c7597cb0b80e1a275f1e7dd..685bbafdf1f326e4c7183f96ade1ed60c0e14abd 100644 (file)
@@ -78,7 +78,6 @@ static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
 static bool arg_force = false;
 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
-static const char* arg_dkr_index_url = NULL;
 static const char* arg_format = NULL;
 static const char *arg_uid = NULL;
 static char **arg_setenv = NULL;
@@ -2166,78 +2165,6 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
         return transfer_image_common(bus, m);
 }
 
-static int pull_dkr(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        const char *local, *remote, *tag;
-        sd_bus *bus = userdata;
-        int r;
-
-        if (arg_verify != IMPORT_VERIFY_NO) {
-                log_error("Imports from DKR do not support image verification, please pass --verify=no.");
-                return -EINVAL;
-        }
-
-        remote = argv[1];
-        tag = strchr(remote, ':');
-        if (tag) {
-                remote = strndupa(remote, tag - remote);
-                tag++;
-        }
-
-        if (!dkr_name_is_valid(remote)) {
-                log_error("DKR name '%s' is invalid.", remote);
-                return -EINVAL;
-        }
-        if (tag && !dkr_tag_is_valid(tag)) {
-                log_error("DKR tag '%s' is invalid.", remote);
-                return -EINVAL;
-        }
-
-        if (argc >= 3)
-                local = argv[2];
-        else {
-                local = strchr(remote, '/');
-                if (local)
-                        local++;
-                else
-                        local = remote;
-        }
-
-        if (isempty(local) || streq(local, "-"))
-                local = NULL;
-
-        if (local) {
-                if (!machine_name_is_valid(local)) {
-                        log_error("Local name %s is not a suitable machine name.", local);
-                        return -EINVAL;
-                }
-        }
-
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "PullDkr");
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        r = sd_bus_message_append(
-                        m,
-                        "sssssb",
-                        arg_dkr_index_url,
-                        remote,
-                        tag,
-                        local,
-                        import_verify_to_string(arg_verify),
-                        arg_force);
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        return transfer_image_common(bus, m);
-}
-
 typedef struct TransferInfo {
         uint32_t id;
         const char *type;
@@ -2452,9 +2379,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "                              json-pretty, json-sse, cat)\n"
                "      --verify=MODE           Verification mode for downloaded images (no,\n"
                "                              checksum, signature)\n"
-               "      --force                 Download image even if already exists\n"
-               "      --dkr-index-url=URL     Specify the index URL to use for DKR image\n"
-               "                              downloads\n\n"
+               "      --force                 Download image even if already exists\n\n"
                "Machine Commands:\n"
                "  list                        List running VMs and containers\n"
                "  status NAME...              Show VM/container details\n"
@@ -2486,7 +2411,6 @@ static int help(int argc, char *argv[], void *userdata) {
                "Image Transfer Commands:\n"
                "  pull-tar URL [NAME]         Download a TAR container image\n"
                "  pull-raw URL [NAME]         Download a RAW container or VM image\n"
-               "  pull-dkr REMOTE [NAME]      Download a DKR container image\n"
                "  import-tar FILE [NAME]      Import a local TAR container image\n"
                "  import-raw FILE [NAME]      Import a local RAW container or VM image\n"
                "  export-tar NAME [FILE]      Export a TAR container image locally\n"
@@ -2510,7 +2434,6 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_ASK_PASSWORD,
                 ARG_VERIFY,
                 ARG_FORCE,
-                ARG_DKR_INDEX_URL,
                 ARG_FORMAT,
                 ARG_UID,
                 ARG_SETENV,
@@ -2536,7 +2459,6 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
                 { "verify",          required_argument, NULL, ARG_VERIFY          },
                 { "force",           no_argument,       NULL, ARG_FORCE           },
-                { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
                 { "format",          required_argument, NULL, ARG_FORMAT          },
                 { "uid",             required_argument, NULL, ARG_UID             },
                 { "setenv",          required_argument, NULL, ARG_SETENV          },
@@ -2650,15 +2572,6 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_force = true;
                         break;
 
-                case ARG_DKR_INDEX_URL:
-                        if (!http_url_is_valid(optarg)) {
-                                log_error("Index URL is invalid: %s", optarg);
-                                return -EINVAL;
-                        }
-
-                        arg_dkr_index_url = optarg;
-                        break;
-
                 case ARG_FORMAT:
                         if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
                                 log_error("Unknown format: %s", optarg);
@@ -2725,7 +2638,6 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
                 { "export-raw",      2,        3,        0,            export_raw        },
                 { "pull-tar",        2,        3,        0,            pull_tar          },
                 { "pull-raw",        2,        3,        0,            pull_raw          },
-                { "pull-dkr",        2,        3,        0,            pull_dkr          },
                 { "list-transfers",  VERB_ANY, 1,        0,            list_transfers    },
                 { "cancel-transfer", 2,        VERB_ANY, 0,            cancel_transfer   },
                 { "set-limit",       2,        3,        0,            set_limit         },
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 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 0f154d97988231b3b8c7adecad3be4716933dd4d..2cabfeaefac66ec71428602d2d346eb036114598 100644 (file)
 #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)
 
 static int arg_family = AF_UNSPEC;
 static int arg_ifindex = 0;
-static int arg_type = 0;
+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];
@@ -320,8 +328,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 +342,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,8 +354,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
         if (r < 0)
                 return bus_log_create_error(r);
 
-        assert((uint16_t) arg_type == arg_type);
-        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);
 
@@ -369,7 +375,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,17 +405,13 @@ static int resolve_record(sd_bus *bus, const char *name) {
                 if (r < 0)
                         return log_oom();
 
-                r = dns_packet_read_rr(p, &rr, NULL);
-                if (r < 0) {
-                        log_error("Failed to parse RR.");
-                        return r;
-                }
+                r = dns_packet_read_rr(p, &rr, NULL, NULL);
+                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))
@@ -439,6 +441,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;
@@ -640,6 +763,125 @@ 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;
+
+        assert(bus);
+
+        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;
@@ -684,6 +926,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);
 }
 
@@ -696,20 +940,24 @@ 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           },
+                { "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 },
                 {}
         };
 
@@ -758,13 +1006,15 @@ static int parse_argv(int argc, char *argv[]) {
                                 return 0;
                         }
 
-                        arg_type = dns_type_from_string(optarg);
-                        if (arg_type < 0) {
+                        r = dns_type_from_string(optarg);
+                        if (r < 0) {
                                 log_error("Failed to parse RR record type %s", optarg);
-                                return arg_type;
+                                return r;
                         }
-                        assert(arg_type > 0 && (uint16_t) arg_type == arg_type);
+                        arg_type = (uint16_t) r;
+                        assert((int) arg_type == r);
 
+                        arg_mode = MODE_RESOLVE_RECORD;
                         break;
 
                 case 'c':
@@ -773,11 +1023,13 @@ static int parse_argv(int argc, char *argv[]) {
                                 return 0;
                         }
 
-                        r = dns_class_from_string(optarg, &arg_class);
+                        r = dns_class_from_string(optarg);
                         if (r < 0) {
                                 log_error("Failed to parse RR record class %s", optarg);
                                 return r;
                         }
+                        arg_class = (uint16_t) r;
+                        assert((int) arg_class == r);
 
                         break;
 
@@ -806,7 +1058,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:
@@ -849,6 +1101,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;
 
@@ -861,7 +1121,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;
         }
@@ -869,6 +1129,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 */;
 }
 
@@ -883,20 +1146,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;
@@ -914,27 +1218,27 @@ int main(int argc, char **argv) {
                         goto finish;
                 }
 
-                goto finish;
-        }
+                break;
 
-        while (argv[optind]) {
-                int family, ifindex, k;
-                union in_addr_union a;
-
-                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..33f4dd9
--- /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
+  https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
+Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV)
+D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
+Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6
+Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types
+  https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
+  https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
+  https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
+! 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
+  https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
+Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers
+Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
+Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework
+Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol
+Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements
+Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones
+  https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification
+Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
+  https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS
+! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
+Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names
+  https://tools.ietf.org/html/rfc6762 → Multicast DNS
+  https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery
+  https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
+  https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
+Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0))
+Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status
+Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC)
+  https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
+Y https://tools.ietf.org/html/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 63b4b36e88d0b0fae45ef94c41e2ae4f77429d8f..fb8228048dab1d1d8b01dc4dbf4cab42c35ea424 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include "dns-type.h"
+#include "string-util.h"
 
 typedef const struct {
         uint16_t type;
@@ -44,7 +45,141 @@ int dns_type_from_string(const char *s) {
         return sc->id;
 }
 
-/* XXX: find an authoritative list of all pseudo types? */
-bool dns_type_is_pseudo(int n) {
-        return IN_SET(n, DNS_TYPE_ANY, DNS_TYPE_AXFR, DNS_TYPE_IXFR, DNS_TYPE_OPT);
+bool dns_type_is_pseudo(uint16_t type) {
+
+        /* Checks whether the specified type is a "pseudo-type". What
+         * a "pseudo-type" precisely is, is defined only very weakly,
+         * but apparently entails all RR types that are not actually
+         * stored as RRs on the server and should hence also not be
+         * cached. We use this list primarily to validate NSEC type
+         * bitfields, and to verify what to cache. */
+
+        return IN_SET(type,
+                      0, /* A Pseudo RR type, according to RFC 2931 */
+                      DNS_TYPE_ANY,
+                      DNS_TYPE_AXFR,
+                      DNS_TYPE_IXFR,
+                      DNS_TYPE_OPT,
+                      DNS_TYPE_TSIG,
+                      DNS_TYPE_TKEY
+        );
+}
+
+bool dns_class_is_pseudo(uint16_t class) {
+        return class == DNS_TYPE_ANY;
+}
+
+bool dns_type_is_valid_query(uint16_t type) {
+
+        /* The types valid as questions in packets */
+
+        return !IN_SET(type,
+                       0,
+                       DNS_TYPE_OPT,
+                       DNS_TYPE_TSIG,
+                       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) {
+
+        /* The types valid as RR in packets (but not necessarily
+         * stored on servers). */
+
+        return !IN_SET(type,
+                       DNS_TYPE_ANY,
+                       DNS_TYPE_AXFR,
+                       DNS_TYPE_IXFR);
+}
+
+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_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) {
+
+        case DNS_CLASS_IN:
+                return "IN";
+
+        case DNS_CLASS_ANY:
+                return "ANY";
+        }
+
+        return NULL;
+}
+
+int dns_class_from_string(const char *s) {
+
+        if (!s)
+                return _DNS_CLASS_INVALID;
+
+        if (strcaseeq(s, "IN"))
+                return DNS_CLASS_IN;
+        else if (strcaseeq(s, "ANY"))
+                return DNS_CLASS_ANY;
+
+        return _DNS_CLASS_INVALID;
 }
index 950af36ee30027b91df4d7b61012950d3c83dece..45080fd243a3c6f7390562497b53194b244250e2 100644 (file)
 
 #include "macro.h"
 
-const char *dns_type_to_string(int type);
-int dns_type_from_string(const char *s);
-bool dns_type_is_pseudo(int n);
-
 /* DNS record types, taken from
  * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
  */
@@ -119,3 +115,28 @@ enum {
 assert_cc(DNS_TYPE_SSHFP == 44);
 assert_cc(DNS_TYPE_TLSA == 52);
 assert_cc(DNS_TYPE_ANY == 255);
+
+/* DNS record classes, see RFC 1035 */
+enum {
+        DNS_CLASS_IN   = 0x01,
+        DNS_CLASS_ANY  = 0xFF,
+
+        _DNS_CLASS_MAX,
+        _DNS_CLASS_INVALID = -1
+};
+
+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_class_is_pseudo(uint16_t class);
+bool dns_class_is_valid_rr(uint16_t class);
+
+const char *dns_type_to_string(int type);
+int dns_type_from_string(const char *s);
+
+const char *dns_class_to_string(uint16_t type);
+int dns_class_from_string(const char *name);
index 0ceca5637102d4a87ac031aff5219bf0b0e30450..41f90dedfd82779d5d6ee4772898a3fe38cb6916 100644 (file)
@@ -60,7 +60,17 @@ static int reply_query_state(DnsQuery *q) {
         case DNS_TRANSACTION_ABORTED:
                 return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
 
-        case DNS_TRANSACTION_FAILURE: {
+        case DNS_TRANSACTION_DNSSEC_FAILED:
+                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)
@@ -84,6 +94,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");
@@ -150,7 +161,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
         }
         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);
@@ -305,7 +316,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
         }
         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);
@@ -471,7 +482,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
         }
         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);
@@ -550,6 +561,11 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
         if (r == 0)
                 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, "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)
                 return r;
@@ -886,7 +902,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. */
@@ -962,10 +978,11 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
         }
         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) {
+                bool has_root_domain = false;
                 DnsResourceRecord *rr;
                 int ifindex;
 
@@ -979,6 +996,11 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
                         if (rr->key->type != DNS_TYPE_SRV)
                                 continue;
 
+                        if (dns_name_is_root(rr->srv.name)) {
+                                has_root_domain = true;
+                                continue;
+                        }
+
                         if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
                                 q->block_all_complete ++;
                                 r = resolve_service_hostname(q, rr, ifindex);
@@ -990,6 +1012,18 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
 
                         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_question_first_name(q->question));
+                        goto finish;
+                }
+
         }
 
         if (found <= 0) {
@@ -1207,16 +1241,101 @@ 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_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 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("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_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_VTABLE_END,
 };
 
@@ -1274,6 +1393,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;
         }
 
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 4db67f7278f4f7ac9a8806e7b7f91f2a9a5509f9..b50558e2808558e069a62f4d717bef06faca18e4 100644 (file)
@@ -22,6 +22,7 @@
 #include "alloc-util.h"
 #include "dns-domain.h"
 #include "resolved-dns-answer.h"
+#include "resolved-dns-dnssec.h"
 #include "string-util.h"
 
 DnsAnswer *dns_answer_new(unsigned n) {
@@ -46,6 +47,18 @@ DnsAnswer *dns_answer_ref(DnsAnswer *a) {
         return a;
 }
 
+static void dns_answer_flush(DnsAnswer *a) {
+        DnsResourceRecord *rr;
+
+        if (!a)
+                return;
+
+        DNS_ANSWER_FOREACH(rr, a)
+                dns_resource_record_unref(rr);
+
+        a->n_rrs = 0;
+}
+
 DnsAnswer *dns_answer_unref(DnsAnswer *a) {
         if (!a)
                 return NULL;
@@ -53,11 +66,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
         assert(a->n_ref > 0);
 
         if (a->n_ref == 1) {
-                unsigned i;
-
-                for (i = 0; i < a->n_rrs; i++)
-                        dns_resource_record_unref(a->items[i].rr);
-
+                dns_answer_flush(a);
                 free(a);
         } else
                 a->n_ref--;
@@ -65,7 +74,39 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a) {
         return NULL;
 }
 
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
+static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
+        assert(rr);
+
+        if (!a)
+                return -ENOSPC;
+
+        if (a->n_rrs >= a->n_allocated)
+                return -ENOSPC;
+
+        a->items[a->n_rrs++] = (DnsAnswerItem) {
+                .rr = dns_resource_record_ref(rr),
+                .ifindex = ifindex,
+                .flags = flags,
+        };
+
+        return 1;
+}
+
+static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+        int ifindex, r;
+
+        DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
+                r = dns_answer_add_raw(a, rr, ifindex, flags);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
         unsigned i;
         int r;
 
@@ -73,6 +114,8 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
 
         if (!a)
                 return -ENOSPC;
+        if (a->n_ref > 1)
+                return -EBUSY;
 
         for (i = 0; i < a->n_rrs; i++) {
                 if (a->items[i].ifindex != ifindex)
@@ -82,27 +125,66 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
                 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;
                         }
 
+                        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;
+                }
         }
 
-        if (a->n_rrs >= a->n_allocated)
-                return -ENOSPC;
+        return dns_answer_add_raw(a, rr, ifindex, flags);
+}
 
-        a->items[a->n_rrs].rr = dns_resource_record_ref(rr);
-        a->items[a->n_rrs].ifindex = ifindex;
-        a->n_rrs++;
+static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+        int ifindex, r;
 
-        return 1;
+        DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
+                r = dns_answer_add(a, rr, ifindex, flags);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
+        int r;
+
+        assert(a);
+        assert(rr);
+
+        r = dns_answer_reserve_or_clone(a, 1);
+        if (r < 0)
+                return r;
+
+        return dns_answer_add(*a, rr, ifindex, flags);
 }
 
 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
@@ -128,101 +210,449 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
         soa->soa.expire = 1;
         soa->soa.minimum = ttl;
 
-        return dns_answer_add(a, soa, 0);
+        return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
 }
 
-int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
-        unsigned i;
+int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
+        DnsAnswerFlags flags = 0, i_flags;
+        DnsResourceRecord *i;
+        bool found = false;
         int r;
 
         assert(key);
 
-        if (!a)
-                return 0;
+        DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+                r = dns_resource_key_match_rr(key, i, NULL);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
 
-        for (i = 0; i < a->n_rrs; i++) {
-                r = dns_resource_key_match_rr(key, a->items[i].rr, NULL);
+                if (!ret_flags)
+                        return 1;
+
+                if (found)
+                        flags &= i_flags;
+                else {
+                        flags = i_flags;
+                        found = true;
+                }
+        }
+
+        if (ret_flags)
+                *ret_flags = flags;
+
+        return found;
+}
+
+int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
+        DnsAnswerFlags flags = 0, i_flags;
+        DnsResourceRecord *i;
+        bool found = false;
+        int r;
+
+        assert(rr);
+
+        DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+                r = dns_resource_record_equal(i, rr);
                 if (r < 0)
                         return r;
-                if (r > 0)
+                if (r == 0)
+                        continue;
+
+                if (!ret_flags)
                         return 1;
+
+                if (found)
+                        flags &= i_flags;
+                else {
+                        flags = i_flags;
+                        found = true;
+                }
         }
 
-        return 0;
+        if (ret_flags)
+                *ret_flags = flags;
+
+        return found;
 }
 
-int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa) {
-        if (soa->class != DNS_CLASS_IN)
-                return 0;
+int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
+        DnsAnswerFlags flags = 0, i_flags;
+        DnsResourceRecord *i;
+        bool found = false;
+        int r;
+
+        assert(key);
+
+        DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
+                r = dns_resource_key_equal(i->key, key);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
 
-        if (soa->type != DNS_TYPE_SOA)
+                if (!ret_flags)
+                        return true;
+
+                if (found)
+                        flags &= i_flags;
+                else {
+                        flags = i_flags;
+                        found = true;
+                }
+        }
+
+        if (ret_flags)
+                *ret_flags = flags;
+
+        return found;
+}
+
+int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
+        DnsResourceRecord *i;
+
+        DNS_ANSWER_FOREACH(i, a) {
+                if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
+                        return true;
+        }
+
+        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);
+
+        /* For a SOA record we can never find a matching SOA record */
+        if (key->type == DNS_TYPE_SOA)
                 return 0;
 
-        if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
+        DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
+                r = dns_resource_key_match_soa(key, rr->key);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+
+                        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;
+                }
+        }
+
+        if (!soa)
                 return 0;
 
+        if (ret)
+                *ret = soa;
+        if (flags)
+                *flags = soa_flags;
+
         return 1;
 }
 
-int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
-        unsigned i;
+int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
+        DnsResourceRecord *rr;
+        DnsAnswerFlags rr_flags;
+        int r;
 
         assert(key);
+
+        /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
+        if (!dns_type_may_redirect(key->type))
+                return 0;
+
+        DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
+                r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        if (ret)
+                                *ret = rr;
+                        if (flags)
+                                *flags = rr_flags;
+                        return 1;
+                }
+        }
+
+        return 0;
+}
+
+int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
+        int r;
+
         assert(ret);
 
-        if (!a)
+        if (dns_answer_size(a) <= 0) {
+                *ret = dns_answer_ref(b);
                 return 0;
+        }
 
-        /* For a SOA record we can never find a matching SOA record */
-        if (key->type == DNS_TYPE_SOA)
+        if (dns_answer_size(b) <= 0) {
+                *ret = dns_answer_ref(a);
                 return 0;
+        }
 
-        for (i = 0; i < a->n_rrs; i++) {
+        k = dns_answer_new(a->n_rrs + b->n_rrs);
+        if (!k)
+                return -ENOMEM;
 
-                if (dns_answer_match_soa(key, a->items[i].rr->key)) {
-                        *ret = a->items[i].rr;
-                        return 1;
-                }
-        }
+        r = dns_answer_add_raw_all(k, a);
+        if (r < 0)
+                return r;
+
+        r = dns_answer_add_all(k, b);
+        if (r < 0)
+                return r;
+
+        *ret = k;
+        k = NULL;
 
         return 0;
 }
 
-DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
-        _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
-        DnsAnswer *k;
+int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
+        DnsAnswer *merged;
+        int r;
+
+        assert(a);
+
+        r = dns_answer_merge(*a, b, &merged);
+        if (r < 0)
+                return r;
+
+        dns_answer_unref(*a);
+        *a = merged;
+
+        return 0;
+}
+
+int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
+        bool found = false, other = false;
+        DnsResourceRecord *rr;
         unsigned i;
         int r;
 
-        if (a && (!b || b->n_rrs <= 0))
-                return dns_answer_ref(a);
-        if ((!a || a->n_rrs <= 0) && b)
-                return dns_answer_ref(b);
+        assert(a);
+        assert(key);
 
-        ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
-        if (!ret)
-                return NULL;
+        /* Remove all entries matching the specified key from *a */
 
-        if (a) {
-                for (i = 0; i < a->n_rrs; i++) {
-                        r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
+        DNS_ANSWER_FOREACH(rr, *a) {
+                r = dns_resource_key_equal(rr->key, key);
+                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_key_equal(rr->key, key);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
+                        r = dns_answer_add_raw(copy, rr, ifindex, flags);
                         if (r < 0)
-                                return NULL;
+                                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_key_equal((*a)->items[i].rr->key, key);
+                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_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 (b) {
-                for (i = 0; i < b->n_rrs; i++) {
-                        r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
+        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 NULL;
+                                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;
+        DnsAnswerFlags flags_source;
+
+        assert(a);
+        assert(key);
+
+        /* Copy all RRs matching the specified key from source into *a */
+
+        DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
+
+                r = dns_resource_key_equal(rr_source->key, key);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                /* Make space for at least one entry */
+                r = dns_answer_reserve_or_clone(a, 1);
+                if (r < 0)
+                        return r;
+
+                r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
+                if (r < 0)
+                        return r;
         }
 
-        k = ret;
-        ret = NULL;
+        return 0;
+}
+
+int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
+        int r;
+
+        assert(to);
+        assert(from);
+        assert(key);
+
+        r = dns_answer_copy_by_key(to, *from, key, or_flags);
+        if (r < 0)
+                return r;
 
-        return k;
+        return dns_answer_remove_by_key(from, key);
 }
 
 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
@@ -261,6 +691,8 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
         DnsAnswer *n;
 
+        assert(a);
+
         if (n_free <= 0)
                 return 0;
 
@@ -275,6 +707,9 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
                 if ((*a)->n_allocated >= ns)
                         return 0;
 
+                /* Allocate more than we need */
+                ns *= 2;
+
                 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
                 if (!n)
                         return -ENOMEM;
@@ -289,3 +724,73 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
         *a = n;
         return 0;
 }
+
+int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
+        int r;
+
+        assert(a);
+
+        /* Tries to extend the DnsAnswer object. And if that's not
+         * possibly, since we are not the sole owner, then allocate a
+         * new, appropriately sized one. Either way, after this call
+         * the object will only have a single reference, and has room
+         * for at least the specified number of RRs. */
+
+        r = dns_answer_reserve(a, n_free);
+        if (r != -EBUSY)
+                return r;
+
+        assert(*a);
+
+        n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
+        if (!n)
+                return -ENOMEM;
+
+        r = dns_answer_add_raw_all(n, *a);
+        if (r < 0)
+                return r;
+
+        dns_answer_unref(*a);
+        *a = n;
+        n = NULL;
+
+        return 0;
+}
+
+void dns_answer_dump(DnsAnswer *answer, FILE *f) {
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+        int ifindex;
+
+        if (!f)
+                f = stdout;
+
+        DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
+                const char *t;
+
+                fputc('\t', f);
+
+                t = dns_resource_record_to_string(rr);
+                if (!t) {
+                        log_oom();
+                        continue;
+                }
+
+                fputs(t, f);
+
+                if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
+                        fputs("\t;", f);
+
+                if (ifindex != 0)
+                        printf(" ifindex=%i", ifindex);
+                if (flags & DNS_ANSWER_AUTHENTICATED)
+                        fputs(" authenticated", f);
+                if (flags & DNS_ANSWER_CACHEABLE)
+                        fputs(" cachable", f);
+                if (flags & DNS_ANSWER_SHARED_OWNER)
+                        fputs(" shared-owner", f);
+
+                fputc('\n', f);
+        }
+}
index 89c254b02ef145eb319ba1ac5c8a00e68369aad7..715e487d94ba27d3000d5e12b5cfa783da2ed9b1 100644 (file)
@@ -30,11 +30,20 @@ typedef struct DnsAnswerItem DnsAnswerItem;
 /* A simple array of resource records. We keep track of the
  * originating ifindex for each RR where that makes sense, so that we
  * can qualify A and AAAA RRs referring to a local link with the
- * right ifindex. */
+ * right ifindex.
+ *
+ * Note that we usually encode the the empty DnsAnswer object as a simple NULL. */
+
+typedef enum DnsAnswerFlags {
+        DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */
+        DNS_ANSWER_CACHEABLE     = 2, /* Item is subject to caching */
+        DNS_ANSWER_SHARED_OWNER  = 4, /* For mDNS: RRset may be owner by multiple peers */
+} DnsAnswerFlags;
 
 struct DnsAnswerItem {
         DnsResourceRecord *rr;
         int ifindex;
+        DnsAnswerFlags flags;
 };
 
 struct DnsAnswer {
@@ -47,16 +56,37 @@ DnsAnswer *dns_answer_new(unsigned n);
 DnsAnswer *dns_answer_ref(DnsAnswer *a);
 DnsAnswer *dns_answer_unref(DnsAnswer *a);
 
-int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex);
+int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
+int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags);
 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl);
-int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
-int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa);
-int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
 
-DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
+int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
+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_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);
+
+int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret);
+int dns_answer_extend(DnsAnswer **a, DnsAnswer *b);
+
 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
 
 int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
+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);
+
+static inline unsigned dns_answer_size(DnsAnswer *a) {
+        return a ? a->n_rrs : 0;
+}
+
+void dns_answer_dump(DnsAnswer *answer, FILE *f);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
 
@@ -70,13 +100,43 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
 
 #define DNS_ANSWER_FOREACH(kk, a) _DNS_ANSWER_FOREACH(UNIQ, kk, a)
 
-#define _DNS_ANSWER_FOREACH_IFINDEX(q, kk, ifindex, a)                  \
+#define _DNS_ANSWER_FOREACH_IFINDEX(q, kk, ifi, a)                      \
         for (unsigned UNIQ_T(i, q) = ({                                 \
                                 (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
-                                (ifindex) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
+                                (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
                                 0;                                      \
                         });                                             \
              (a) && (UNIQ_T(i, q) < (a)->n_rrs);                        \
-             UNIQ_T(i, q)++, (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), (ifindex) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0))
+             UNIQ_T(i, q)++,                                            \
+                     (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+                     (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0))
 
 #define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) _DNS_ANSWER_FOREACH_IFINDEX(UNIQ, kk, ifindex, a)
+
+#define _DNS_ANSWER_FOREACH_FLAGS(q, kk, fl, a)                         \
+        for (unsigned UNIQ_T(i, q) = ({                                 \
+                                (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+                                (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
+                                0;                                      \
+                        });                                             \
+             (a) && (UNIQ_T(i, q) < (a)->n_rrs);                        \
+             UNIQ_T(i, q)++,                                            \
+                     (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+                     (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
+
+#define DNS_ANSWER_FOREACH_FLAGS(kk, flags, a) _DNS_ANSWER_FOREACH_FLAGS(UNIQ, kk, flags, a)
+
+#define _DNS_ANSWER_FOREACH_FULL(q, kk, ifi, fl, a)                     \
+        for (unsigned UNIQ_T(i, q) = ({                                 \
+                                (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \
+                                (ifi) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \
+                                (fl) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].flags : 0; \
+                                0;                                      \
+                        });                                             \
+             (a) && (UNIQ_T(i, q) < (a)->n_rrs);                        \
+             UNIQ_T(i, q)++,                                            \
+                     (kk) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].rr : NULL), \
+                     (ifi) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].ifindex : 0), \
+                     (fl) = ((UNIQ_T(i, q) < (a)->n_rrs) ? (a)->items[UNIQ_T(i, q)].flags : 0))
+
+#define DNS_ANSWER_FOREACH_FULL(kk, ifindex, flags, a) _DNS_ANSWER_FOREACH_FULL(UNIQ, kk, ifindex, flags, a)
index 6124ff659c9679ce74e2336089d7dac1422a1b58..301f383809c20bb5decc809bf2a5833151b9574c 100644 (file)
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "resolved-dns-answer.h"
 #include "resolved-dns-cache.h"
 #include "resolved-dns-packet.h"
 #include "string-util.h"
 
-/* Never cache more than 1K entries */
-#define CACHE_MAX 1024
+/* 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 10min in our cache */
-#define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
+/* We never keep any item longer than 2h in our cache */
+#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
 
 typedef enum DnsCacheItemType DnsCacheItemType;
 typedef struct DnsCacheItem DnsCacheItem;
@@ -41,14 +43,18 @@ enum DnsCacheItemType {
 };
 
 struct DnsCacheItem {
+        DnsCacheItemType type;
         DnsResourceKey *key;
         DnsResourceRecord *rr;
+
         usec_t until;
-        DnsCacheItemType type;
-        unsigned prioq_idx;
-        bool authenticated;
+        bool authenticated:1;
+        bool shared_owner:1;
+
         int owner_family;
         union in_addr_union owner_address;
+
+        unsigned prioq_idx;
         LIST_FIELDS(DnsCacheItem, by_key);
 };
 
@@ -63,7 +69,7 @@ static void dns_cache_item_free(DnsCacheItem *i) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
 
-static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
+static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
         DnsCacheItem *first;
 
         assert(c);
@@ -84,34 +90,55 @@ static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
         dns_cache_item_free(i);
 }
 
-void dns_cache_flush(DnsCache *c) {
-        DnsCacheItem *i;
+static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
+        DnsCacheItem *first, *i;
+        int r;
+
+        first = hashmap_get(c->by_key, rr->key);
+        LIST_FOREACH(by_key, i, first) {
+                r = dns_resource_record_equal(i->rr, rr);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        dns_cache_item_unlink_and_free(c, i);
+                        return true;
+                }
+        }
+
+        return false;
+}
+
+static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
+        DnsCacheItem *first, *i, *n;
 
         assert(c);
+        assert(key);
 
-        while ((i = hashmap_first(c->by_key)))
-                dns_cache_item_remove_and_free(c, i);
+        first = hashmap_remove(c->by_key, key);
+        if (!first)
+                return false;
 
-        assert(hashmap_size(c->by_key) == 0);
-        assert(prioq_size(c->by_expiry) == 0);
+        LIST_FOREACH_SAFE(by_key, i, n, first) {
+                prioq_remove(c->by_expiry, i, &i->prioq_idx);
+                dns_cache_item_free(i);
+        }
 
-        c->by_key = hashmap_free(c->by_key);
-        c->by_expiry = prioq_free(c->by_expiry);
+        return true;
 }
 
-static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
-        DnsCacheItem *i;
-        bool exist = false;
+void dns_cache_flush(DnsCache *c) {
+        DnsResourceKey *key;
 
         assert(c);
-        assert(key);
 
-        while ((i = hashmap_get(c->by_key, key))) {
-                dns_cache_item_remove_and_free(c, i);
-                exist = true;
-        }
+        while ((key = hashmap_first_key(c->by_key)))
+                dns_cache_remove_by_key(c, key);
 
-        return exist;
+        assert(hashmap_size(c->by_key) == 0);
+        assert(prioq_size(c->by_expiry) == 0);
+
+        c->by_key = hashmap_free(c->by_key);
+        c->by_expiry = prioq_free(c->by_expiry);
 }
 
 static void dns_cache_make_space(DnsCache *c, unsigned add) {
@@ -141,7 +168,7 @@ static void dns_cache_make_space(DnsCache *c, unsigned add) {
                 /* Take an extra reference to the key so that it
                  * doesn't go away in the middle of the remove call */
                 key = dns_resource_key_ref(i->key);
-                dns_cache_remove(c, key);
+                dns_cache_remove_by_key(c, key);
         }
 }
 
@@ -153,7 +180,6 @@ void dns_cache_prune(DnsCache *c) {
         /* Remove all entries that are past their TTL */
 
         for (;;) {
-                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
                 DnsCacheItem *i;
 
                 i = prioq_peek(c->by_expiry);
@@ -166,10 +192,19 @@ void dns_cache_prune(DnsCache *c) {
                 if (i->until > t)
                         break;
 
-                /* Take an extra reference to the key so that it
-                 * doesn't go away in the middle of the remove call */
-                key = dns_resource_key_ref(i->key);
-                dns_cache_remove(c, key);
+                /* Depending whether this is an mDNS shared entry
+                 * either remove only this one RR or the whole
+                 * RRset */
+                if (i->shared_owner)
+                        dns_cache_item_unlink_and_free(c, i);
+                else {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+
+                        /* Take an extra reference to the key so that it
+                         * doesn't go away in the middle of the remove call */
+                        key = dns_resource_key_ref(i->key);
+                        dns_cache_remove_by_key(c, key);
+                }
         }
 }
 
@@ -238,10 +273,56 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
         return NULL;
 }
 
-static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
+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,
+                DnsResourceRecord *rr,
+                bool authenticated,
+                bool shared_owner,
+                usec_t timestamp,
+                int owner_family,
+                const union in_addr_union *owner_address) {
+
         assert(c);
         assert(i);
         assert(rr);
+        assert(owner_address);
 
         i->type = DNS_CACHE_POSITIVE;
 
@@ -258,8 +339,12 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
         dns_resource_key_unref(i->key);
         i->key = dns_resource_key_ref(rr->key);
 
+        i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
         i->authenticated = authenticated;
-        i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
+        i->shared_owner = shared_owner;
+
+        i->owner_family = owner_family;
+        i->owner_address = *owner_address;
 
         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
 }
@@ -268,6 +353,7 @@ static int dns_cache_put_positive(
                 DnsCache *c,
                 DnsResourceRecord *rr,
                 bool authenticated,
+                bool shared_owner,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
@@ -275,35 +361,48 @@ static int dns_cache_put_positive(
         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
         _cleanup_free_ char *key_str = NULL;
         DnsCacheItem *existing;
-        int r;
+        int r, k;
 
         assert(c);
         assert(rr);
         assert(owner_address);
 
-        /* New TTL is 0? Delete the entry... */
+        /* Never cache pseudo RRs */
+        if (dns_class_is_pseudo(rr->key->class))
+                return 0;
+        if (dns_type_is_pseudo(rr->key->type))
+                return 0;
+
+        /* New TTL is 0? Delete this specific entry... */
         if (rr->ttl <= 0) {
-                r = dns_resource_key_to_string(rr->key, &key_str);
-                if (r < 0)
-                        return r;
+                k = dns_cache_remove_by_rr(c, rr);
 
-                if (dns_cache_remove(c, rr->key))
-                        log_debug("Removed zero TTL entry from cache: %s", key_str);
-                else
-                        log_debug("Not caching zero TTL cache entry: %s", key_str);
+                if (log_get_max_level() >= LOG_DEBUG) {
+                        r = dns_resource_key_to_string(rr->key, &key_str);
+                        if (r < 0)
+                                return r;
 
-                return 0;
-        }
+                        if (k > 0)
+                                log_debug("Removed zero TTL entry from cache: %s", key_str);
+                        else
+                                log_debug("Not caching zero TTL cache entry: %s", key_str);
+                }
 
-        if (rr->key->class == DNS_CLASS_ANY)
-                return 0;
-        if (rr->key->type == DNS_TYPE_ANY)
                 return 0;
+        }
 
-        /* Entry exists already? Update TTL and timestamp */
+        /* Entry exists already? Update TTL, timestamp and owner*/
         existing = dns_cache_get(c, rr);
         if (existing) {
-                dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
+                dns_cache_item_update_positive(
+                                c,
+                                existing,
+                                rr,
+                                authenticated,
+                                shared_owner,
+                                timestamp,
+                                owner_family,
+                                owner_address);
                 return 0;
         }
 
@@ -321,21 +420,24 @@ 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->prioq_idx = PRIOQ_IDX_NULL;
+        i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
+        i->authenticated = authenticated;
+        i->shared_owner = shared_owner;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
-        i->authenticated = authenticated;
+        i->prioq_idx = PRIOQ_IDX_NULL;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
                 return r;
 
-        r = dns_resource_key_to_string(i->key, &key_str);
-        if (r < 0)
-                return r;
+        if (log_get_max_level() >= LOG_DEBUG) {
+                r = dns_resource_key_to_string(i->key, &key_str);
+                if (r < 0)
+                        return r;
 
-        log_debug("Added cache entry for %s", key_str);
+                log_debug("Added positive cache entry for %s", key_str);
+        }
 
         i = NULL;
         return 0;
@@ -346,8 +448,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) {
 
@@ -357,20 +460,25 @@ static int dns_cache_put_negative(
 
         assert(c);
         assert(key);
+        assert(soa);
         assert(owner_address);
 
-        dns_cache_remove(c, key);
-
-        if (key->class == DNS_CLASS_ANY)
+        /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
+         * important to filter out as we use this as a pseudo-type for
+         * NXDOMAIN entries */
+        if (dns_class_is_pseudo(key->class))
                 return 0;
-        if (key->type == DNS_TYPE_ANY)
+        if (dns_type_is_pseudo(key->type))
                 return 0;
-        if (soa_ttl <= 0) {
-                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);
+        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/NSEC/NSEC3 TTL: %s", key_str);
+                }
 
                 return 0;
         }
@@ -389,65 +497,117 @@ static int dns_cache_put_negative(
                 return -ENOMEM;
 
         i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN;
-        i->key = dns_resource_key_ref(key);
-        i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
-        i->prioq_idx = PRIOQ_IDX_NULL;
+        i->until = calculate_until(soa, nsec_ttl, timestamp, true);
+        i->authenticated = authenticated;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
-        i->authenticated = authenticated;
+        i->prioq_idx = PRIOQ_IDX_NULL;
+
+        if (i->type == DNS_CACHE_NXDOMAIN) {
+                /* NXDOMAIN entries should apply equally to all types, so we use ANY as
+                 * a pseudo type for this purpose here. */
+                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);
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
                 return r;
 
-        r = dns_resource_key_to_string(i->key, &key_str);
-        if (r < 0)
-                return r;
+        if (log_get_max_level() >= LOG_DEBUG) {
+                r = dns_resource_key_to_string(i->key, &key_str);
+                if (r < 0)
+                        return r;
 
-        log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
+                log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
+        }
 
         i = NULL;
         return 0;
 }
 
+static void dns_cache_remove_previous(
+                DnsCache *c,
+                DnsResourceKey *key,
+                DnsAnswer *answer) {
+
+        DnsResourceRecord *rr;
+        DnsAnswerFlags flags;
+
+        assert(c);
+
+        /* First, if we were passed a key (i.e. on LLMNR/DNS, but
+         * not on mDNS), delete all matching old RRs, so that we only
+         * keep complete by_key in place. */
+        if (key)
+                dns_cache_remove_by_key(c, key);
+
+        /* Second, flush all entries matching the answer, unless this
+         * is an RR that is explicitly marked to be "shared" between
+         * peers (i.e. mDNS RRs without the flush-cache bit set). */
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+                        continue;
+
+                if (flags & DNS_ANSWER_SHARED_OWNER)
+                        continue;
+
+                dns_cache_remove_by_key(c, rr->key);
+        }
+}
+
 int dns_cache_put(
                 DnsCache *c,
                 DnsResourceKey *key,
                 int rcode,
                 DnsAnswer *answer,
-                unsigned max_rrs,
                 bool authenticated,
+                uint32_t nsec_ttl,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
 
-        DnsResourceRecord *soa = NULL;
-        unsigned cache_keys, i;
+        DnsResourceRecord *soa = NULL, *rr;
+        DnsAnswerFlags flags;
+        unsigned cache_keys;
         int r;
 
         assert(c);
+        assert(owner_address);
 
-        if (key) {
-                /* First, if we were passed a key, delete all matching old RRs,
-                 * so that we only keep complete by_key in place. */
-                dns_cache_remove(c, key);
-        }
+        dns_cache_remove_previous(c, key, answer);
 
-        if (!answer)
-                return 0;
+        if (dns_answer_size(answer) <= 0) {
+                if (log_get_max_level() >= LOG_DEBUG) {
+                        _cleanup_free_ char *key_str = NULL;
+
+                        r = dns_resource_key_to_string(key, &key_str);
+                        if (r < 0)
+                                return r;
+
+                        log_debug("Not caching negative entry without a SOA record: %s", key_str);
+                }
 
-        for (i = 0; i < answer->n_rrs; i++)
-                dns_cache_remove(c, answer->items[i].rr->key);
+                return 0;
+        }
 
         /* We only care for positive replies and NXDOMAINs, on all
          * other replies we will simply flush the respective entries,
          * and that's it */
-
         if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
                 return 0;
 
-        cache_keys = answer->n_rrs;
-
+        cache_keys = dns_answer_size(answer);
         if (key)
                 cache_keys ++;
 
@@ -458,63 +618,63 @@ int dns_cache_put(
                 timestamp = now(clock_boottime_or_monotonic());
 
         /* Second, add in positive entries for all contained RRs */
-        for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
-                DnsResourceRecord *rr = answer->items[i].rr;
-
-                if (rr->key->cache_flush)
-                        dns_cache_remove(c, rr->key);
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+                        continue;
 
-                r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address);
+                r = dns_cache_put_positive(
+                                c,
+                                rr,
+                                flags & DNS_ANSWER_AUTHENTICATED,
+                                flags & DNS_ANSWER_SHARED_OWNER,
+                                timestamp,
+                                owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
 
-        if (!key)
+        if (!key) /* mDNS doesn't know negative caching, really */
                 return 0;
 
         /* Third, add in negative entries if the key has no RR */
-        r = dns_answer_contains(answer, key);
+        r = dns_answer_match_key(answer, key, NULL);
         if (r < 0)
                 goto fail;
         if (r > 0)
                 return 0;
 
-        /* See https://tools.ietf.org/html/rfc2308, which
-         * say that a matching SOA record in the packet
-         * is used to to enable negative caching. */
+        /* But not if it has a matching CNAME/DNAME (the negative
+         * caching will be done on the canonical name, not on the
+         * alias) */
+        r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
+        if (r < 0)
+                goto fail;
+        if (r > 0)
+                return 0;
 
-        r = dns_answer_find_soa(answer, key, &soa);
+        /* 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;
         if (r == 0)
                 return 0;
 
-        /* Also, if the requested key is an alias, the negative response should
-           be cached for each name in the redirect chain. Any CNAME record in
-           the response is from the redirection chain, though only the final one
-           is guaranteed to be included. This means that we cannot verify the
-           chain and that we need to cache them all as it may be incomplete. */
-        for (i = 0; i < answer->n_rrs; i++) {
-                DnsResourceRecord *answer_rr = answer->items[i].rr;
-
-                if (answer_rr->key->type == DNS_TYPE_CNAME) {
-                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *canonical_key = NULL;
-
-                        canonical_key = dns_resource_key_new_redirect(key, answer_rr);
-                        if (!canonical_key)
-                                goto fail;
-
-                        /* Let's not add negative cache entries for records outside the current zone. */
-                        if (!dns_answer_match_soa(canonical_key, soa->key))
-                                continue;
-
-                        r = dns_cache_put_negative(c, canonical_key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
-                        if (r < 0)
-                                goto fail;
-                }
-        }
+        /* Refuse using the SOA data if it is unsigned, but the key is
+         * signed */
+        if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
+                return 0;
 
-        r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+        r = dns_cache_put_negative(
+                        c,
+                        key,
+                        rcode,
+                        authenticated,
+                        nsec_ttl,
+                        timestamp,
+                        soa,
+                        owner_family, owner_address);
         if (r < 0)
                 goto fail;
 
@@ -525,10 +685,14 @@ fail:
          * added, just in case */
 
         if (key)
-                dns_cache_remove(c, key);
+                dns_cache_remove_by_key(c, key);
 
-        for (i = 0; i < answer->n_rrs; i++)
-                dns_cache_remove(c, answer->items[i].rr->key);
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
+                        continue;
+
+                dns_cache_remove_by_key(c, rr->key);
+        }
 
         return r;
 }
@@ -545,36 +709,44 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
          * much, after all this is just a cache */
 
         i = hashmap_get(c->by_key, k);
-        if (i || IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME, DNS_TYPE_NSEC))
+        if (i)
                 return i;
 
         n = DNS_RESOURCE_KEY_NAME(k);
 
-        /* Check if we have an NSEC record instead for the name. */
-        i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
-        if (i)
+        /* Check if we have an NXDOMAIN cache item for the name, notice that we use
+         * the pseudo-type ANY for NXDOMAIN cache items. */
+        i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
+        if (i && i->type == DNS_CACHE_NXDOMAIN)
                 return i;
 
-        /* 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)
-                return i;
+        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)
+                        return i;
 
-        /* OK, let's look for cached DNAME records. */
-        for (;;) {
-                char label[DNS_LABEL_MAX];
+                /* OK, let's look for cached DNAME records. */
+                for (;;) {
+                        if (isempty(n))
+                                return NULL;
 
-                if (isempty(n))
-                        return NULL;
+                        i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
+                        if (i)
+                                return i;
+
+                        /* Jump one label ahead */
+                        r = dns_name_parent(&n);
+                        if (r <= 0)
+                                return NULL;
+                }
+        }
 
-                i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
+        if (k->type != DNS_TYPE_NSEC) {
+                /* Check if we have an NSEC record instead for the name. */
+                i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
                 if (i)
                         return i;
-
-                /* Jump one label ahead */
-                r = dns_label_unescape(&n, label, sizeof(label));
-                if (r <= 0)
-                        return NULL;
         }
 
         return NULL;
@@ -601,11 +773,15 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                 /* If we have ANY lookups we don't use the cache, so
                  * that the caller refreshes via the network. */
 
-                r = dns_resource_key_to_string(key, &key_str);
-                if (r < 0)
-                        return r;
+                if (log_get_max_level() >= LOG_DEBUG) {
+                        r = dns_resource_key_to_string(key, &key_str);
+                        if (r < 0)
+                                return r;
 
-                log_debug("Ignoring cache for ANY lookup: %s", key_str);
+                        log_debug("Ignoring cache for ANY lookup: %s", key_str);
+                }
+
+                c->n_miss++;
 
                 *ret = NULL;
                 *rcode = DNS_RCODE_SUCCESS;
@@ -616,11 +792,15 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
         if (!first) {
                 /* If one question cannot be answered we need to refresh */
 
-                r = dns_resource_key_to_string(key, &key_str);
-                if (r < 0)
-                        return r;
+                if (log_get_max_level() >= LOG_DEBUG) {
+                        r = dns_resource_key_to_string(key, &key_str);
+                        if (r < 0)
+                                return r;
 
-                log_debug("Cache miss for %s", key_str);
+                        log_debug("Cache miss for %s", key_str);
+                }
+
+                c->n_miss++;
 
                 *ret = NULL;
                 *rcode = DNS_RCODE_SUCCESS;
@@ -642,32 +822,48 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                         have_non_authenticated = true;
         }
 
-        r = dns_resource_key_to_string(key, &key_str);
-        if (r < 0)
-                return r;
-
         if (nsec && key->type != DNS_TYPE_NSEC) {
-                log_debug("NSEC NODATA cache hit for %s", key_str);
+                if (log_get_max_level() >= LOG_DEBUG) {
+                        r = dns_resource_key_to_string(key, &key_str);
+                        if (r < 0)
+                                return r;
+
+                        log_debug("NSEC NODATA cache hit for %s", key_str);
+                }
 
                 /* We only found an NSEC record that matches our name.
-                 * If it says the type doesn't exit report
+                 * If it says the type doesn't exist report
                  * NODATA. Otherwise report a cache miss. */
 
                 *ret = NULL;
                 *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;
         }
 
-        log_debug("%s cache hit for %s",
-                  n > 0    ? "Positive" :
-                  nxdomain ? "NXDOMAIN" : "NODATA",
-                  key_str);
+        if (log_get_max_level() >= LOG_DEBUG) {
+                r = dns_resource_key_to_string(key, &key_str);
+                if (r < 0)
+                        return r;
+
+                log_debug("%s cache hit for %s",
+                          n > 0    ? "Positive" :
+                          nxdomain ? "NXDOMAIN" : "NODATA",
+                          key_str);
+        }
 
         if (n <= 0) {
+                c->n_hit++;
+
                 *ret = NULL;
                 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
                 *authenticated = have_authenticated && !have_non_authenticated;
@@ -682,11 +878,13 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
                 if (!j->rr)
                         continue;
 
-                r = dns_answer_add(answer, j->rr, 0);
+                r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
                 if (r < 0)
                         return r;
         }
 
+        c->n_hit++;
+
         *ret = answer;
         *rcode = DNS_RCODE_SUCCESS;
         *authenticated = have_authenticated && !have_non_authenticated;
@@ -738,20 +936,36 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
         int r;
 
         assert(cache);
+        assert(p);
 
         HASHMAP_FOREACH(i, cache->by_key, iterator) {
                 DnsCacheItem *j;
 
                 LIST_FOREACH(by_key, j, i) {
-                        _cleanup_free_ char *t = NULL;
-
                         if (!j->rr)
                                 continue;
 
-                        if (!dns_key_is_shared(j->rr->key))
+                        if (!j->shared_owner)
                                 continue;
 
                         r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+                        if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
+                                /* For mDNS, if we're unable to stuff all known answers into the given packet,
+                                 * allocate a new one, push the RR into that one and link it to the current one.
+                                 */
+
+                                DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+                                ancount = 0;
+
+                                r = dns_packet_new_query(&p->more, p->protocol, 0, true);
+                                if (r < 0)
+                                        return r;
+
+                                /* continue with new packet */
+                                p = p->more;
+                                r = dns_packet_append_rr(p, j->rr, NULL, NULL);
+                        }
+
                         if (r < 0)
                                 return r;
 
@@ -779,13 +993,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;
                                 }
@@ -793,13 +1007,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);
@@ -814,3 +1029,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 0f28bbe543ba581942d610b1937aabf3ec15e443..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, unsigned max_rrs, 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 2d06775dca4036e62e754021d8f718678176c79a..43fcbe146051f61669544cde22f0bfb82d94f332 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "hexdecoct.h"
 #include "resolved-dns-dnssec.h"
 #include "resolved-dns-packet.h"
 #include "string-table.h"
  *
  * TODO:
  *
- *   - Iterative validation
- *   - NSEC proof of non-existance
- *   - NSEC3 proof of non-existance
- *   - Make trust anchor store read additional DS+DNSKEY data from disk
- *   - wildcard zones compatibility
+ *   - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
  *   - multi-label zone compatibility
- *   - DMSSEC cname/dname compatibility
- *   - per-interface DNSSEC setting
- *   - DSA support
- *   - EC support?
+ *   - cname/dname compatibility
+ *   - nxdomain on qname
+ *   - bus calls to override DNSEC setting per interface
+ *   - log all DNSSEC downgrades
+ *   - enable by default
  *
+ *   - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies)
+ *   - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone)
+ *   - RFC 6840, Section 4.3 (check for CNAME on NSEC too)
  * */
 
 #define VERIFY_RRS_MAX 256
@@ -53,6 +54,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:
  *
  *            Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
  */
 
-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);
-}
+static void initialize_libgcrypt(void) {
+        const char *p;
+
+        if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+                return;
+
+        p = gcry_check_version("1.4.5");
+        assert(p);
 
-static bool dnssec_digest_supported(int digest) {
-        return IN_SET(digest,
-                      DNSSEC_DIGEST_SHA1,
-                      DNSSEC_DIGEST_SHA256);
+        gcry_control(GCRYCTL_DISABLE_SECMEM);
+        gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
 }
 
-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. */
@@ -88,8 +91,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;
 
@@ -115,21 +122,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,
@@ -193,11 +200,12 @@ static int dnssec_rsa_verify(
         }
 
         ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
-        if (ge == GPG_ERR_BAD_SIGNATURE)
+        if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
                 r = 0;
-        else if (ge != 0)
+        else if (ge != 0) {
+                log_debug("RSA signature check failed: %s", gpg_strerror(ge));
                 r = -EIO;
-        else
+        else
                 r = 1;
 
 finish:
@@ -218,6 +226,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));
 }
@@ -267,24 +465,60 @@ 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;
+        }
+}
+
 int dnssec_verify_rrset(
                 DnsAnswer *a,
-                DnsResourceKey *key,
+                const DnsResourceKey *key,
                 DnsResourceRecord *rrsig,
                 DnsResourceRecord *dnskey,
-                usec_t realtime) {
+                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;
+        size_t hash_size;
+        void *hash;
         DnsResourceRecord **list, *rr;
         gcry_md_hd_t md = NULL;
+        int r, md_algorithm;
+        bool wildcard = false;
         size_t k, n = 0;
-        int r;
 
         assert(key);
         assert(rrsig);
         assert(dnskey);
+        assert(result);
         assert(rrsig->key->type == DNS_TYPE_RRSIG);
         assert(dnskey->key->type == DNS_TYPE_DNSKEY);
 
@@ -292,20 +526,24 @@ 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))
-                return -EOPNOTSUPP;
-
-        if (a->n_rrs > VERIFY_RRS_MAX)
-                return -E2BIG;
+        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;
 
         r = dnssec_rrsig_expired(rrsig, realtime);
         if (r < 0)
                 return r;
-        if (r > 0)
-                return DNSSEC_SIGNATURE_EXPIRED;
+        if (r > 0) {
+                *result = DNSSEC_SIGNATURE_EXPIRED;
+                return 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);
@@ -320,37 +558,24 @@ int dnssec_verify_rrset(
                         return r;
 
                 list[n++] = rr;
+
+                if (n > VERIFY_RRS_MAX)
+                        return -E2BIG;
         }
 
         if (n <= 0)
                 return -ENODATA;
 
         /* Bring the RRs into canonical order */
-        qsort_safe(list, n, sizeof(DnsResourceRecord), rr_compare);
+        qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
 
         /* 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;
+        initialize_libgcrypt();
 
-        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;
-
-        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;
 
@@ -368,10 +593,19 @@ int dnssec_verify_rrset(
         gcry_md_write(md, wire_format_name, r);
 
         for (k = 0; k < n; k++) {
+                const char *suffix;
                 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);
+                r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
+                if (r < 0)
+                        goto finish;
+                if (r > 0) /* This is a wildcard! */ {
+                        gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
+                        wildcard = true;
+                }
+
+                r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
                 if (r < 0)
                         goto finish;
                 gcry_md_write(md, wire_format_name, r);
@@ -380,12 +614,11 @@ int dnssec_verify_rrset(
                 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);
@@ -394,64 +627,47 @@ int dnssec_verify_rrset(
                 goto finish;
         }
 
-        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)[0]) << 8) |
-                        ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
-
-                if (exponent_size < 256) {
-                        r = -EINVAL;
-                        goto finish;
-                }
-
-                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;
-                }
+        switch (rrsig->rrsig.algorithm) {
 
-                if (1 + exponent_size >= dnskey->dnskey.key_size) {
-                        r = -EINVAL;
-                        goto finish;
-                }
+        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;
 
-                modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
-                modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
+        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;
         }
 
-        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;
 
-        r = r ? DNSSEC_VERIFIED : DNSSEC_INVALID;
+        if (!r)
+                *result = DNSSEC_INVALID;
+        else if (wildcard)
+                *result = DNSSEC_VALIDATED_WILDCARD;
+        else
+                *result = DNSSEC_VALIDATED;
+        r = 0;
 
 finish:
         gcry_md_close(md);
         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);
@@ -468,18 +684,22 @@ 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), DNS_RESOURCE_KEY_NAME(rrsig->key));
+        return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
 }
 
-int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
+        int r;
+
         assert(key);
         assert(rrsig);
 
@@ -492,22 +712,61 @@ int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
         if (rrsig->rrsig.type_covered != key->type)
                 return 0;
 
+        /* Make sure signer is a parent of the RRset */
+        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
+        if (r <= 0)
+                return r;
+
+        /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
+        r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
+        if (r < 0)
+                return r;
+        if (r < rrsig->rrsig.labels)
+                return 0;
+
         return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
 }
 
+static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
+        DnsResourceRecord *rr;
+        int r;
+
+        assert(key);
+        assert(rrsig);
+
+        DNS_ANSWER_FOREACH(rr, a) {
+                r = dns_resource_key_equal(key, rr->key);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                /* Pick the TTL as the minimum of the RR's TTL, the
+                 * RR's original TTL according to the RRSIG and the
+                 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
+                rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
+                rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+        }
+
+        return 0;
+}
+
 int dnssec_verify_rrset_search(
                 DnsAnswer *a,
-                DnsResourceKey *key,
+                const DnsResourceKey *key,
                 DnsAnswer *validated_dnskeys,
-                usec_t realtime) {
+                usec_t realtime,
+                DnssecResult *result,
+                DnsResourceRecord **ret_rrsig) {
 
-        bool found_rrsig = false, found_dnskey = false;
+        bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
         DnsResourceRecord *rrsig;
         int r;
 
         assert(key);
+        assert(result);
 
-        /* Verifies all RRs from "a" that match the key "key", against DNSKEY RRs in "validated_dnskeys" */
+        /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
 
         if (!a || a->n_rrs <= 0)
                 return -ENODATA;
@@ -515,7 +774,9 @@ int dnssec_verify_rrset_search(
         /* Iterate through each RRSIG RR. */
         DNS_ANSWER_FOREACH(rrsig, a) {
                 DnsResourceRecord *dnskey;
+                DnsAnswerFlags flags;
 
+                /* Is this an RRSIG RR that applies to RRs matching our key? */
                 r = dnssec_key_match_rrsig(key, rrsig);
                 if (r < 0)
                         return r;
@@ -524,16 +785,20 @@ int dnssec_verify_rrset_search(
 
                 found_rrsig = true;
 
-                DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
+                /* Look for a matching key */
+                DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
+                        DnssecResult one_result;
+
+                        if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+                                continue;
 
-                        r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
+                        /* Is this a DNSKEY RR that matches they key of our RRSIG? */
+                        r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
                         if (r < 0)
                                 return r;
                         if (r == 0)
                                 continue;
 
-                        found_dnskey = true;
-
                         /* Take the time here, if it isn't set yet, so
                          * that we do all validations with the same
                          * time. */
@@ -545,42 +810,102 @@ int dnssec_verify_rrset_search(
                          * the RRSet against the RRSIG and DNSKEY
                          * combination. */
 
-                        r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime);
-                        if (r < 0 && r != EOPNOTSUPP)
+                        r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
+                        if (r < 0)
                                 return r;
-                        if (r == DNSSEC_VERIFIED)
-                                return DNSSEC_VERIFIED;
-
-                        /* If the signature is invalid, or done using
-                           an unsupported algorithm, let's try another
-                           key and/or signature. After all they
-                           key_tags and stuff are not unique, and
-                           might be shared by multiple keys. */
+
+                        switch (one_result) {
+
+                        case DNSSEC_VALIDATED:
+                        case DNSSEC_VALIDATED_WILDCARD:
+                                /* Yay, the RR has been validated,
+                                 * return immediately, but fix up the expiry */
+                                r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
+                                if (r < 0)
+                                        return r;
+
+                                if (ret_rrsig)
+                                        *ret_rrsig = rrsig;
+
+                                *result = one_result;
+                                return 0;
+
+                        case DNSSEC_INVALID:
+                                /* If the signature is invalid, let's try another
+                                   key and/or signature. After all they
+                                   key_tags and stuff are not unique, and
+                                   might be shared by multiple keys. */
+                                found_invalid = true;
+                                continue;
+
+                        case DNSSEC_UNSUPPORTED_ALGORITHM:
+                                /* If the key algorithm is
+                                   unsupported, try another
+                                   RRSIG/DNSKEY pair, but remember we
+                                   encountered this, so that we can
+                                   return a proper error when we
+                                   encounter nothing better. */
+                                found_unsupported_algorithm = true;
+                                continue;
+
+                        case DNSSEC_SIGNATURE_EXPIRED:
+                                /* If the signature is expired, try
+                                   another one, but remember it, so
+                                   that we can return this */
+                                found_expired_rrsig = true;
+                                continue;
+
+                        default:
+                                assert_not_reached("Unexpected DNSSEC validation result");
+                        }
                 }
         }
 
-        if (found_dnskey)
-                return DNSSEC_INVALID;
+        if (found_expired_rrsig)
+                *result = DNSSEC_SIGNATURE_EXPIRED;
+        else if (found_unsupported_algorithm)
+                *result = DNSSEC_UNSUPPORTED_ALGORITHM;
+        else if (found_invalid)
+                *result = DNSSEC_INVALID;
+        else if (found_rrsig)
+                *result = DNSSEC_MISSING_KEY;
+        else
+                *result = DNSSEC_NO_SIGNATURE;
 
-        if (found_rrsig)
-                return DNSSEC_MISSING_KEY;
+        if (ret_rrsig)
+                *ret_rrsig = NULL;
 
-        return DNSSEC_NO_SIGNATURE;
+        return 0;
 }
 
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
-        size_t c = 0;
+int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
+        DnsResourceRecord *rr;
         int r;
 
-        /* Converts the specified hostname into DNSSEC canonicalized
-         * form. */
+        /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
 
-        if (buffer_max < 2)
-                return -ENOBUFS;
+        DNS_ANSWER_FOREACH(rr, a) {
+                r = dnssec_key_match_rrsig(key, rr);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 1;
+        }
 
-        for (;;) {
-                size_t i;
+        return 0;
+}
+
+int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
+        size_t c = 0;
+        int r;
+
+        /* Converts the specified hostname into DNSSEC canonicalized
+         * form. */
+
+        if (buffer_max < 2)
+                return -ENOBUFS;
 
+        for (;;) {
                 r = dns_label_unescape(&n, buffer, buffer_max);
                 if (r < 0)
                         return r;
@@ -607,11 +932,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;
@@ -633,11 +954,32 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
         return (int) c;
 }
 
-int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
-        gcry_md_hd_t md = NULL;
+static int digest_to_gcrypt_md(uint8_t algorithm) {
+
+        /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
+
+        switch (algorithm) {
+
+        case DNSSEC_DIGEST_SHA1:
+                return GCRY_MD_SHA1;
+
+        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, bool mask_revoke) {
         char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
+        gcry_md_hd_t md = NULL;
+        size_t hash_size;
+        int md_algorithm, r;
         void *result;
-        int r;
 
         assert(dnskey);
         assert(ds);
@@ -650,50 +992,41 @@ 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 (!dnssec_algorithm_supported(dnskey->dnskey.algorithm))
-                return -EOPNOTSUPP;
-        if (!dnssec_digest_supported(ds->ds.digest_type))
-                return -EOPNOTSUPP;
-
         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;
 
-        switch (ds->ds.digest_type) {
-
-        case DNSSEC_DIGEST_SHA1:
-
-                if (ds->ds.digest_size != 20)
-                        return 0;
+        initialize_libgcrypt();
 
-                gcry_md_open(&md, GCRY_MD_SHA1, 0);
-                break;
-
-        case DNSSEC_DIGEST_SHA256:
+        md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
+        if (md_algorithm < 0)
+                return md_algorithm;
 
-                if (ds->ds.digest_size != 32)
-                        return 0;
+        hash_size = gcry_md_get_algo_dlen(md_algorithm);
+        assert(hash_size > 0);
 
-                gcry_md_open(&md, GCRY_MD_SHA256, 0);
-                break;
+        if (ds->ds.digest_size != hash_size)
+                return 0;
 
-        default:
-                assert_not_reached("Unknown digest");
-        }
+        r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
+        if (r < 0)
+                return r;
 
+        gcry_md_open(&md, md_algorithm, 0);
         if (!md)
                 return -EIO;
 
-        r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
-        if (r < 0)
-                goto finish;
-
         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);
@@ -711,9 +1044,640 @@ finish:
         return r;
 }
 
-static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
-        [DNSSEC_NO] = "no",
-        [DNSSEC_TRUST] = "trust",
-        [DNSSEC_YES] = "yes",
+int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
+        DnsResourceRecord *ds;
+        DnsAnswerFlags flags;
+        int r;
+
+        assert(dnskey);
+
+        if (dnskey->key->type != DNS_TYPE_DNSKEY)
+                return 0;
+
+        DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
+
+                if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
+                        continue;
+
+                if (ds->key->type != DNS_TYPE_DS)
+                        continue;
+
+                if (ds->key->class != dnskey->key->class)
+                        continue;
+
+                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(dnskey, ds, false);
+                if (r == -EKEYREJECTED)
+                        return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 1;
+        }
+
+        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;
+        size_t hash_size;
+        int algorithm;
+        void *result;
+        unsigned k;
+        int r;
+
+        assert(nsec3);
+        assert(name);
+        assert(ret);
+
+        if (nsec3->key->type != DNS_TYPE_NSEC3)
+                return -EINVAL;
+
+        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;
+
+        initialize_libgcrypt();
+
+        hash_size = gcry_md_get_algo_dlen(algorithm);
+        assert(hash_size > 0);
+
+        if (nsec3->nsec3.next_hashed_name_size != hash_size)
+                return -EINVAL;
+
+        r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
+        if (r < 0)
+                return r;
+
+        gcry_md_open(&md, algorithm, 0);
+        if (!md)
+                return -EIO;
+
+        gcry_md_write(md, wire_format, r);
+        gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
+
+        result = gcry_md_read(md, 0);
+        if (!result) {
+                r = -EIO;
+                goto finish;
+        }
+
+        for (k = 0; k < nsec3->nsec3.iterations; k++) {
+                uint8_t tmp[hash_size];
+                memcpy(tmp, result, hash_size);
+
+                gcry_md_reset(md);
+                gcry_md_write(md, tmp, hash_size);
+                gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
+
+                result = gcry_md_read(md, 0);
+                if (!result) {
+                        r = -EIO;
+                        goto finish;
+                }
+        }
+
+        memcpy(ret, result, hash_size);
+        r = (int) hash_size;
+
+finish:
+        gcry_md_close(md);
+        return r;
+}
+
+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;
+
+        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;
+
+        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];
+        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 = NULL, *wildcard_domain = NULL;
+        const char *zone, *p, *pp = NULL;
+        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, 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(zone_rr, flags, answer) {
+                        r = nsec3_is_good(zone_rr, NULL);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                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;
+                }
+
+                /* 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 = nsec3_is_good(enclosure_rr, zone_rr);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
+                                continue;
+
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) {
+                                a = flags & DNS_ANSWER_AUTHENTICATED;
+                                goto found_closest_encloser;
+                        }
+                }
+
+                /* We didn't find the closest encloser with this name,
+                 * but let's remember this domain name, it might be
+                 * the next closer name */
+
+                pp = p;
+
+                /* Strip one label from the front */
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+        }
+
+        *result = DNSSEC_NSEC_NO_RR;
+        return 0;
+
+found_closest_encloser:
+        /* We found a closest encloser in 'p'; next closer is 'pp' */
+
+        /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
+        if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
+                return -EBADMSG;
+
+        /* Ensure that this data is from the delegated domain
+         * (i.e. originates from the "lower" DNS server), and isn't
+         * just glue records (i.e. doesn't originate from the "upper"
+         * DNS server). */
+        if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+            !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+                return -EBADMSG;
+
+        if (!pp) {
+                /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
+                if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
+                        *result = DNSSEC_NSEC_FOUND;
+                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;
+        }
+
+        /* Prove that there is no next closer and whether or not there is a wildcard domain. */
+
+        wildcard = strappend("*.", p);
+        if (!wildcard)
+                return -ENOMEM;
+
+        r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
+        if (r < 0)
+                return r;
+        if (r != hashed_size)
+                return -EBADMSG;
+
+        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 *next_hashed_domain = NULL;
+
+                r = nsec3_is_good(rr, zone_rr);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                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), next_closer_domain, next_hashed_domain);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        if (rr->nsec3.flags & 1)
+                                optout = true;
+
+                        a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+                        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)
+                                /* 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;
+
+                        a = a && (flags & DNS_ANSWER_AUTHENTICATED);
+
+                        no_wildcard = true;
+                }
+        }
+
+        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_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
+        DnsResourceRecord *rr;
+        bool have_nsec3 = false;
+        DnsAnswerFlags flags;
+        int r;
+
+        assert(key);
+        assert(result);
+
+        /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
+
+        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
+
+                if (rr->key->class != key->class)
+                        continue;
+
+                switch (rr->key->type) {
+
+                case DNS_TYPE_NSEC:
+
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
+                        if (r < 0)
+                                return r;
+                        if (r > 0) {
+                                if (bitmap_isset(rr->nsec.types, key->type))
+                                        *result = DNSSEC_NSEC_FOUND;
+                                else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
+                                        *result = DNSSEC_NSEC_CNAME;
+                                else
+                                        *result = DNSSEC_NSEC_NODATA;
+
+                                if (authenticated)
+                                        *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+                                if (ttl)
+                                        *ttl = rr->ttl;
+
+                                return 0;
+                        }
+
+                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) {
+                                *result = DNSSEC_NSEC_NXDOMAIN;
+
+                                if (authenticated)
+                                        *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+                                if (ttl)
+                                        *ttl = rr->ttl;
+
+                                return 0;
+                        }
+                        break;
+
+                case DNS_TYPE_NSEC3:
+                        have_nsec3 = true;
+                        break;
+                }
+        }
+
+        /* 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_between(DnsAnswer *answer, 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;
+
+                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                switch (rr->key->type) {
+
+                case DNS_TYPE_NSEC:
+                        r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
+                        if (r < 0)
+                                return r;
+
+                        found = r > 0;
+                        break;
+
+                case DNS_TYPE_NSEC3: {
+                        _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
+
+                        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;
+                }
+
+                default:
+                        continue;
+                }
+
+                if (found) {
+                        if (authenticated)
+                                *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
+                        return 1;
+                }
+        }
+
+        return 0;
+}
+
+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",
+        [DNSSEC_NO_SIGNATURE] = "no-signature",
+        [DNSSEC_MISSING_KEY] = "missing-key",
+        [DNSSEC_UNSIGNED] = "unsigned",
+        [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
+        [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
+        [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
 };
-DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
+DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
index f4cb58988a7c05a1a6aa0946f27ed9d3d0988c12..8a9bcf5b91a2f93a80beafc9ffa90d134b356f6d 100644 (file)
 ***/
 
 typedef enum DnssecMode DnssecMode;
+typedef enum DnssecResult DnssecResult;
 
 #include "dns-domain.h"
 #include "resolved-dns-answer.h"
 #include "resolved-dns-rr.h"
 
-enum DnssecMode {
-        /* No DNSSEC validation is done */
-        DNSSEC_NO,
-
-        /* Trust the AD bit sent by the server. UNSAFE! */
-        DNSSEC_TRUST,
-
-        /* 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 {
-        DNSSEC_VERIFIED,
+enum DnssecResult {
+        /* 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,
+
+        /* These two are added by dnssec_verify_rrset_search() */
         DNSSEC_NO_SIGNATURE,
         DNSSEC_MISSING_KEY,
-        DNSSEC_SIGNATURE_EXPIRED,
+
+        /* These two are added by the DnsTransaction logic */
+        DNSSEC_UNSIGNED,
+        DNSSEC_FAILED_AUXILIARY,
+        DNSSEC_NSEC_MISMATCH,
+        DNSSEC_INCOMPATIBLE_SERVER,
+
+        _DNSSEC_RESULT_MAX,
+        _DNSSEC_RESULT_INVALID = -1
 };
 
 #define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
 
-int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey);
-int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig);
+/* 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, bool revoked_ok);
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
+
+int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result);
+int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result, DnsResourceRecord **rrsig);
 
-int dnssec_verify_rrset(DnsAnswer *answer, DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime);
-int dnssec_verify_rrset_search(DnsAnswer *a, DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime);
+int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke);
+int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds);
 
-int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *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);
 
-const char* dnssec_mode_to_string(DnssecMode m) _const_;
-DnssecMode dnssec_mode_from_string(const char *s) _pure_;
+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,
+        DNSSEC_NSEC_FOUND,
+        DNSSEC_NSEC_OPTOUT,
+} DnssecNsecResult;
+
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
+int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated);
+
+const char* dnssec_result_to_string(DnssecResult m) _const_;
+DnssecResult dnssec_result_from_string(const char *s) _pure_;
index 9bd08eeec2553b52c90993654a7362db91d41ff2..a8a8632491908108183a1ce7df53ec1960c3b984 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;
@@ -65,20 +66,18 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
         return 0;
 }
 
-int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) {
-        DnsPacket *p;
-        DnsPacketHeader *h;
-        int r;
+void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) {
 
-        assert(ret);
+        DnsPacketHeader *h;
 
-        r = dns_packet_new(&p, protocol, mtu);
-        if (r < 0)
-                return r;
+        assert(p);
 
         h = DNS_PACKET_HEADER(p);
 
-        if (protocol == DNS_PROTOCOL_LLMNR)
+        switch(p->protocol) {
+        case DNS_PROTOCOL_LLMNR:
+                assert(!truncated);
+
                 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
                                                          0 /* opcode */,
                                                          0 /* c */,
@@ -88,17 +87,23 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool
                                                          0 /* ad */,
                                                          0 /* cd */,
                                                          0 /* rcode */));
-        else if (protocol == DNS_PROTOCOL_MDNS)
-                h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
-                                                         0 /* opcode */,
-                                                         0 /* aa */,
-                                                         0 /* tc */,
-                                                         0 /* rd (ask for recursion) */,
-                                                         0 /* ra */,
-                                                         0 /* ad */,
-                                                         0 /* cd */,
-                                                         0 /* rcode */));
-        else
+                break;
+
+        case DNS_PROTOCOL_MDNS:
+                h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0         /* qr */,
+                                                         0         /* opcode */,
+                                                         0         /* aa */,
+                                                         truncated /* tc */,
+                                                         0         /* rd (ask for recursion) */,
+                                                         0         /* ra */,
+                                                         0         /* ad */,
+                                                         0         /* cd */,
+                                                         0         /* rcode */));
+                break;
+
+        default:
+                assert(!truncated);
+
                 h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */,
                                                          0 /* opcode */,
                                                          0 /* aa */,
@@ -108,6 +113,23 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool
                                                          0 /* ad */,
                                                          dnssec_checking_disabled /* cd */,
                                                          0 /* rcode */));
+        }
+}
+
+int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) {
+        DnsPacket *p;
+        int r;
+
+        assert(ret);
+
+        r = dns_packet_new(&p, protocol, mtu);
+        if (r < 0)
+                return r;
+
+        /* Always set the TC bit to 0 initially.
+         * If there are multiple packets later, we'll update the bit shortly before sending.
+         */
+        dns_packet_set_flags(p, dnssec_checking_disabled, false);
 
         *ret = p;
         return 0;
@@ -132,6 +154,7 @@ static void dns_packet_free(DnsPacket *p) {
 
         dns_question_unref(p->question);
         dns_answer_unref(p->answer);
+        dns_resource_record_unref(p->opt);
 
         while ((s = hashmap_steal_first_key(p->names)))
                 free(s);
@@ -149,6 +172,8 @@ DnsPacket *dns_packet_unref(DnsPacket *p) {
 
         assert(p->n_ref > 0);
 
+        dns_packet_unref(p->more);
+
         if (p->n_ref == 1)
                 dns_packet_free(p);
         else
@@ -185,6 +210,7 @@ int dns_packet_validate_reply(DnsPacket *p) {
                 return -EBADMSG;
 
         switch (p->protocol) {
+
         case DNS_PROTOCOL_LLMNR:
                 /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */
                 if (DNS_PACKET_QDCOUNT(p) != 1)
@@ -225,6 +251,7 @@ int dns_packet_validate_query(DnsPacket *p) {
                 return -EBADMSG;
 
         switch (p->protocol) {
+
         case DNS_PROTOCOL_LLMNR:
                 /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */
                 if (DNS_PACKET_QDCOUNT(p) != 1)
@@ -412,10 +439,15 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
         return 0;
 }
 
-int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) {
+int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, bool canonical_candidate, size_t *start) {
         uint8_t *w;
         int r;
 
+        /* Append a label to a packet. Optionally, does this in DNSSEC
+         * canonical form, if this label is marked as a candidate for
+         * it, and the canonical form logic is enabled for the
+         * packet */
+
         assert(p);
         assert(d);
 
@@ -428,18 +460,14 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start
 
         *(w++) = (uint8_t) l;
 
-        if (p->canonical_form) {
+        if (p->canonical_form && canonical_candidate) {
                 size_t i;
 
                 /* 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
@@ -453,6 +481,7 @@ int dns_packet_append_name(
                 DnsPacket *p,
                 const char *name,
                 bool allow_compression,
+                bool canonical_candidate,
                 size_t *start) {
 
         size_t saved_size;
@@ -466,8 +495,8 @@ 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;
@@ -486,12 +515,6 @@ 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;
@@ -507,11 +530,19 @@ int dns_packet_append_name(
                 if (k > 0)
                         r = k;
 
-                r = dns_packet_append_label(p, label, r, &n);
+                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;
@@ -548,7 +579,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start)
 
         saved_size = p->size;
 
-        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL);
+        r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL);
         if (r < 0)
                 goto fail;
 
@@ -570,7 +601,7 @@ fail:
         return r;
 }
 
-static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, uint8_t *types, size_t *start) {
+static int dns_packet_append_type_window(DnsPacket *p, uint8_t window, uint8_t length, const uint8_t *types, size_t *start) {
         size_t saved_size;
         int r;
 
@@ -611,7 +642,6 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
         int r;
 
         assert(p);
-        assert(types);
 
         saved_size = p->size;
 
@@ -627,15 +657,16 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
                 }
 
                 window = n >> 8;
-
                 entry = n & 255;
 
                 bitmaps[entry / 8] |= 1 << (7 - (entry % 8));
         }
 
-        r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
-        if (r < 0)
-                goto fail;
+        if (bitmaps[entry / 8] != 0) {
+                r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL);
+                if (r < 0)
+                        goto fail;
+        }
 
         if (start)
                 *start = saved_size;
@@ -647,7 +678,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;
 
@@ -655,6 +686,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 */
@@ -683,10 +719,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;
 
@@ -697,6 +771,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;
@@ -736,14 +831,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->srv.name, true, NULL);
+                r = dns_packet_append_name(p, rr->srv.name, true, false, NULL);
                 break;
 
         case DNS_TYPE_PTR:
         case DNS_TYPE_NS:
         case DNS_TYPE_CNAME:
         case DNS_TYPE_DNAME:
-                r = dns_packet_append_name(p, rr->ptr.name, true, NULL);
+                r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);
                 break;
 
         case DNS_TYPE_HINFO:
@@ -786,11 +881,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 break;
 
         case DNS_TYPE_SOA:
-                r = dns_packet_append_name(p, rr->soa.mname, true, NULL);
+                r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->soa.rname, true, NULL);
+                r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -818,7 +913,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->mx.exchange, true, NULL);
+                r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);
                 break;
 
         case DNS_TYPE_LOC:
@@ -922,7 +1017,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL);
+                r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -930,7 +1025,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                 break;
 
         case DNS_TYPE_NSEC:
-                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, NULL);
+                r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -939,6 +1034,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                         goto fail;
 
                 break;
+
         case DNS_TYPE_NSEC3:
                 r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL);
                 if (r < 0)
@@ -973,6 +1069,8 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
                         goto fail;
 
                 break;
+
+        case DNS_TYPE_OPT:
         case _DNS_TYPE_INVALID: /* unparseable */
         default:
 
@@ -1009,7 +1107,6 @@ fail:
         return r;
 }
 
-
 int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
         assert(p);
 
@@ -1419,7 +1516,7 @@ fail:
         return r;
 }
 
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_free_ char *name = NULL;
         bool cache_flush = false;
         uint16_t class, type;
@@ -1447,7 +1544,7 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
         if (p->protocol == DNS_PROTOCOL_MDNS) {
                 /* See RFC6762, Section 10.2 */
 
-                if (class & MDNS_RR_CACHE_FLUSH) {
+                if (type != DNS_TYPE_OPT && (class & MDNS_RR_CACHE_FLUSH)) {
                         class &= ~MDNS_RR_CACHE_FLUSH;
                         cache_flush = true;
                 }
@@ -1459,11 +1556,11 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) {
                 goto fail;
         }
 
-        key->cache_flush = cache_flush;
-
         name = NULL;
         *ret = key;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1479,11 +1576,12 @@ static bool loc_size_ok(uint8_t size) {
         return m <= 9 && e <= 9 && (m > 0 || e == 0);
 }
 
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
         size_t saved_rindex, offset;
         uint16_t rdlength;
+        bool cache_flush;
         int r;
 
         assert(p);
@@ -1491,12 +1589,12 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
 
         saved_rindex = p->rindex;
 
-        r = dns_packet_read_key(p, &key, NULL);
+        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
         if (r < 0)
                 goto fail;
 
-        if (key->class == DNS_CLASS_ANY ||
-            key->type == DNS_TYPE_ANY) {
+        if (!dns_class_is_valid_rr(key->class)||
+            !dns_type_is_valid_rr(key->type)) {
                 r = -EBADMSG;
                 goto fail;
         }
@@ -1511,6 +1609,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         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;
@@ -1544,10 +1647,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 r = dns_packet_read_name(p, &rr->ptr.name, true, NULL);
                 break;
 
-        case DNS_TYPE_OPT: /* we only care about the header */
-                r = 0;
-                break;
-
         case DNS_TYPE_HINFO:
                 r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
                 if (r < 0)
@@ -1725,6 +1824,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
                 }
 
                 break;
+
         case DNS_TYPE_SSHFP:
                 r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
                 if (r < 0)
@@ -1822,7 +1922,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         case DNS_TYPE_NSEC: {
 
                 /*
-                 * RFC6762, section 18.14 explicly states mDNS should use name compression.
+                 * RFC6762, section 18.14 explictly states mDNS should use name compression.
                  * This contradicts RFC3845, section 2.1.1
                  */
 
@@ -1887,6 +1987,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
 
                 break;
         }
+
+        case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
         default:
         unparseable:
                 r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.size, NULL);
@@ -1904,6 +2006,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
         *ret = rr;
         rr = NULL;
 
+        if (ret_cache_flush)
+                *ret_cache_flush = cache_flush;
         if (start)
                 *start = saved_rindex;
 
@@ -1936,11 +2040,22 @@ int dns_packet_extract(DnsPacket *p) {
 
                 for (i = 0; i < n; i++) {
                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+                        bool cache_flush;
 
-                        r = dns_packet_read_key(p, &key, NULL);
+                        r = dns_packet_read_key(p, &key, &cache_flush, NULL);
                         if (r < 0)
                                 goto finish;
 
+                        if (cache_flush) {
+                                r = -EBADMSG;
+                                goto finish;
+                        }
+
+                        if (!dns_type_is_valid_query(key->type)) {
+                                r = -EBADMSG;
+                                goto finish;
+                        }
+
                         r = dns_question_add(question, key);
                         if (r < 0)
                                 goto finish;
@@ -1957,14 +2072,49 @@ int dns_packet_extract(DnsPacket *p) {
 
                 for (i = 0; i < n; i++) {
                         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+                        bool cache_flush;
 
-                        r = dns_packet_read_rr(p, &rr, NULL);
+                        r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
                         if (r < 0)
                                 goto finish;
 
-                        r = dns_answer_add(answer, rr, p->ifindex);
-                        if (r < 0)
-                                goto finish;
+                        if (rr->key->type == DNS_TYPE_OPT) {
+
+                                if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) {
+                                        r = -EBADMSG;
+                                        goto finish;
+                                }
+
+                                /* Note that we accept the OPT RR in
+                                 * any section, not just in the
+                                 * additional section, as some routers
+                                 * (Belkin!)  blindly copy the OPT RR
+                                 * from the query to the reply packet,
+                                 * and don't get the section right. */
+
+                                /* Two OPT RRs? */
+                                if (p->opt) {
+                                        r = -EBADMSG;
+                                        goto finish;
+                                }
+
+                                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. */
+
+                                r = dns_answer_add(answer, rr, p->ifindex,
+                                                   (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
+                                                   (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
+                                if (r < 0)
+                                        goto finish;
+                        }
                 }
         }
 
@@ -1983,6 +2133,30 @@ finish:
         return r;
 }
 
+int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
+        int r;
+
+        assert(p);
+        assert(key);
+
+        /* Checks if the specified packet is a reply for the specified
+         * key and the specified key is the only one in the question
+         * section. */
+
+        if (DNS_PACKET_QR(p) != 1)
+                return 0;
+
+        /* Let's unpack the packet, if that hasn't happened yet. */
+        r = dns_packet_extract(p);
+        if (r < 0)
+                return r;
+
+        if (p->question->n_keys != 1)
+                return 0;
+
+        return dns_resource_key_equal(p->question->keys[0], key);
+}
+
 static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
         [DNS_RCODE_SUCCESS] = "SUCCESS",
         [DNS_RCODE_FORMERR] = "FORMERR",
index 48b3572cb4a4e08f4d077ffcda50bc298ef4b3de..6821be73e4aa34c47f9a909709c1dba4bd0b2c8d 100644 (file)
@@ -76,10 +76,12 @@ 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;
         DnsAnswer *answer;
+        DnsResourceRecord *opt;
 
         /* Packet reception metadata */
         int ifindex;
@@ -88,6 +90,9 @@ struct DnsPacket {
         uint16_t sender_port, destination_port;
         uint32_t ttl;
 
+        /* For support of truncated packets */
+        DnsPacket *more;
+
         bool on_stack:1;
         bool extracted:1;
         bool refuse_compression:1;
@@ -114,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)
@@ -146,6 +161,8 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
 int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu);
 int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled);
 
+void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated);
+
 DnsPacket *dns_packet_ref(DnsPacket *p);
 DnsPacket *dns_packet_unref(DnsPacket *p);
 
@@ -155,19 +172,22 @@ int dns_packet_validate(DnsPacket *p);
 int dns_packet_validate_reply(DnsPacket *p);
 int dns_packet_validate_query(DnsPacket *p);
 
+int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key);
+
 int dns_packet_append_blob(DnsPacket *p, const void *d, size_t sz, size_t *start);
 int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start);
 int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start);
 int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start);
 int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start);
 int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start);
-int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start);
-int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, size_t *start);
+int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start);
+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);
@@ -177,8 +197,8 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start);
 int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start);
 int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start);
 int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start);
-int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start);
-int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start);
+int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start);
+int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start);
 
 void dns_packet_rewind(DnsPacket *p, size_t idx);
 
@@ -193,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,
index 089d9fb70d641dc6aa6b1a93446a8d7b6746e342..1948d59fc4bf69baa770822cd1519cf2e71c9386 100644 (file)
@@ -59,7 +59,7 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
         assert(c);
 
         while ((t = set_steal_first(c->transactions))) {
-                set_remove(t->query_candidates, c);
+                set_remove(t->notify_query_candidates, c);
                 dns_transaction_gc(t);
         }
 }
@@ -116,32 +116,35 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
         assert(c);
         assert(key);
 
-        r = set_ensure_allocated(&c->transactions, NULL);
-        if (r < 0)
-                return r;
-
         t = dns_scope_find_transaction(c->scope, key, true);
         if (!t) {
                 r = dns_transaction_new(&t, c->scope, key);
                 if (r < 0)
                         return r;
+        } else {
+                if (set_contains(c->transactions, t))
+                        return 0;
         }
 
-        r = set_ensure_allocated(&t->query_candidates, NULL);
+        r = set_ensure_allocated(&c->transactions, NULL);
+        if (r < 0)
+                goto gc;
+
+        r = set_ensure_allocated(&t->notify_query_candidates, NULL);
         if (r < 0)
                 goto gc;
 
-        r = set_put(t->query_candidates, c);
+        r = set_put(t->notify_query_candidates, c);
         if (r < 0)
                 goto gc;
 
         r = set_put(c->transactions, t);
         if (r < 0) {
-                set_remove(t->query_candidates, c);
+                (void) set_remove(t->notify_query_candidates, c);
                 goto gc;
         }
 
-        return 0;
+        return 1;
 
 gc:
         dns_transaction_gc(t);
@@ -182,9 +185,21 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
 
                 switch (t->state) {
 
-                case DNS_TRANSACTION_PENDING:
                 case DNS_TRANSACTION_NULL:
-                        return t->state;
+                        /* If there's a NULL transaction pending, then
+                         * this means not all transactions where
+                         * started yet, and we were called from within
+                         * the stackframe that is supposed to start
+                         * remaining transactions. In this case,
+                         * simply claim the candidate is pending. */
+
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_VALIDATING:
+                        /* If there's one transaction currently in
+                         * VALIDATING state, then this means there's
+                         * also one in PENDING state, hence we can
+                         * return PENDING immediately. */
+                        return DNS_TRANSACTION_PENDING;
 
                 case DNS_TRANSACTION_SUCCESS:
                         state = t->state;
@@ -233,7 +248,7 @@ fail:
         return r;
 }
 
-void dns_query_candidate_ready(DnsQueryCandidate *c) {
+void dns_query_candidate_notify(DnsQueryCandidate *c) {
         DnsTransactionState state;
         int r;
 
@@ -241,7 +256,7 @@ void dns_query_candidate_ready(DnsQueryCandidate *c) {
 
         state = dns_query_candidate_state(c);
 
-        if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL))
+        if (DNS_TRANSACTION_IS_LIVE(state))
                 return;
 
         if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
@@ -394,8 +409,8 @@ int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
 
 static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
         assert(q);
-        assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
-        assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+        assert(!DNS_TRANSACTION_IS_LIVE(state));
+        assert(DNS_TRANSACTION_IS_LIVE(q->state));
 
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
@@ -539,7 +554,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
 
                 rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
 
-                r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+                r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
         }
@@ -553,7 +568,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
 
                 rr->aaaa.in6_addr = in6addr_loopback;
 
-                r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex));
+                r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
         }
@@ -561,7 +576,7 @@ static int synthesize_localhost_rr(DnsQuery *q, DnsResourceKey *key, DnsAnswer *
         return 0;
 }
 
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex) {
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
 
         rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
@@ -572,7 +587,7 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to,
         if (!rr->ptr.name)
                 return -ENOMEM;
 
-        return dns_answer_add(*answer, rr, ifindex);
+        return dns_answer_add(*answer, rr, ifindex, flags);
 }
 
 static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer **answer) {
@@ -582,12 +597,12 @@ static int synthesize_localhost_ptr(DnsQuery *q, DnsResourceKey *key, DnsAnswer
         assert(key);
         assert(answer);
 
-        r = dns_answer_reserve(answer, 1);
-        if (r < 0)
-                return r;
-
         if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
-                r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+                r = dns_answer_reserve(answer, 1);
+                if (r < 0)
+                        return r;
+
+                r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
         }
@@ -618,7 +633,7 @@ static int answer_add_addresses_rr(
                 if (r < 0)
                         return r;
 
-                r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+                r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
         }
@@ -659,7 +674,7 @@ static int answer_add_addresses_ptr(
                 if (r < 0)
                         return r;
 
-                r = dns_answer_add(*answer, rr, addresses[j].ifindex);
+                r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
         }
@@ -725,15 +740,15 @@ static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_ad
                 if (r < 0)
                         return r;
 
-                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
 
-                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex));
+                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
 
-                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex));
+                r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
                 if (r < 0)
                         return r;
 
@@ -795,7 +810,7 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
         /* Tries to synthesize localhost RR replies where appropriate */
 
         if (!IN_SET(*state,
-                    DNS_TRANSACTION_FAILURE,
+                    DNS_TRANSACTION_RCODE_FAILURE,
                     DNS_TRANSACTION_NO_SERVERS,
                     DNS_TRANSACTION_TIMEOUT,
                     DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
@@ -946,6 +961,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++;
 
@@ -970,9 +987,11 @@ fail:
 
 static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
         DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
+        bool has_authenticated = false, has_non_authenticated = false;
+        DnssecResult dnssec_result_authenticated = _DNSSEC_RESULT_INVALID, dnssec_result_non_authenticated = _DNSSEC_RESULT_INVALID;
         DnsTransaction *t;
         Iterator i;
-        bool has_authenticated = false, has_non_authenticated = false;
+        int r;
 
         assert(q);
 
@@ -988,29 +1007,29 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
 
                 case DNS_TRANSACTION_SUCCESS: {
                         /* We found a successfuly reply, merge it into the answer */
-                        DnsAnswer *merged;
-
-                        merged = dns_answer_merge(q->answer, t->answer);
-                        if (!merged) {
+                        r = dns_answer_extend(&q->answer, t->answer);
+                        if (r < 0) {
                                 dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
                                 return;
                         }
 
-                        dns_answer_unref(q->answer);
-                        q->answer = merged;
                         q->answer_rcode = t->answer_rcode;
 
-                        if (t->answer_authenticated)
+                        if (t->answer_authenticated) {
                                 has_authenticated = true;
-                        else
+                                dnssec_result_authenticated = t->answer_dnssec_result;
+                        } else {
                                 has_non_authenticated = true;
+                                dnssec_result_non_authenticated = t->answer_dnssec_result;
+                        }
 
                         state = DNS_TRANSACTION_SUCCESS;
                         break;
                 }
 
-                case DNS_TRANSACTION_PENDING:
                 case DNS_TRANSACTION_NULL:
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_VALIDATING:
                 case DNS_TRANSACTION_ABORTED:
                         /* Ignore transactions that didn't complete */
                         continue;
@@ -1019,22 +1038,25 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
                         /* Any kind of failure? Store the data away,
                          * if there's nothing stored yet. */
 
-                        if (state != DNS_TRANSACTION_SUCCESS) {
-
-                                dns_answer_unref(q->answer);
-                                q->answer = dns_answer_ref(t->answer);
-                                q->answer_rcode = t->answer_rcode;
+                        if (state == DNS_TRANSACTION_SUCCESS)
+                                continue;
 
-                                state = t->state;
-                        }
+                        q->answer = dns_answer_unref(q->answer);
+                        q->answer_rcode = t->answer_rcode;
+                        q->answer_dnssec_result = t->answer_dnssec_result;
 
+                        state = t->state;
                         break;
                 }
         }
 
+        if (state == DNS_TRANSACTION_SUCCESS) {
+                q->answer_authenticated = has_authenticated && !has_non_authenticated;
+                q->answer_dnssec_result = q->answer_authenticated ? dnssec_result_authenticated : dnssec_result_non_authenticated;
+        }
+
         q->answer_protocol = c->scope->protocol;
         q->answer_family = c->scope->family;
-        q->answer_authenticated = has_authenticated && !has_non_authenticated;
 
         dns_search_domain_unref(q->answer_search_domain);
         q->answer_search_domain = dns_search_domain_ref(c->search_domain);
@@ -1049,7 +1071,7 @@ void dns_query_ready(DnsQuery *q) {
         bool pending = false;
 
         assert(q);
-        assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+        assert(DNS_TRANSACTION_IS_LIVE(q->state));
 
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
@@ -1066,14 +1088,16 @@ void dns_query_ready(DnsQuery *q) {
                 switch (state) {
 
                 case DNS_TRANSACTION_SUCCESS:
-                        /* One of the transactions is successful,
+                        /* One of the candidates is successful,
                          * let's use it, and copy its data out */
                         dns_query_accept(q, c);
                         return;
 
-                case DNS_TRANSACTION_PENDING:
                 case DNS_TRANSACTION_NULL:
-                        /* One of the transactions is still going on, let's maybe wait for it */
+                case DNS_TRANSACTION_PENDING:
+                case DNS_TRANSACTION_VALIDATING:
+                        /* One of the candidates is still going on,
+                         * let's maybe wait for it */
                         pending = true;
                         break;
 
@@ -1096,6 +1120,8 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname)
 
         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;
@@ -1121,8 +1147,8 @@ int dns_query_process_cname(DnsQuery *q) {
 
         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) {
 
@@ -1130,7 +1156,7 @@ int dns_query_process_cname(DnsQuery *q) {
                 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));
                 if (r < 0)
@@ -1140,7 +1166,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;
@@ -1152,20 +1178,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) {
index b71bb2352b97f62509ce64b2655f84f4d9efcb9f..4a0d265a2d30aecb4b6c5e37ccf54f1f83a3aa08 100644 (file)
@@ -72,10 +72,11 @@ struct DnsQuery {
         /* Discovered data */
         DnsAnswer *answer;
         int answer_rcode;
+        DnssecResult answer_dnssec_result;
+        bool answer_authenticated;
         DnsProtocol answer_protocol;
         int answer_family;
         DnsSearchDomain *answer_search_domain;
-        bool answer_authenticated;
 
         /* Bus client information */
         sd_bus_message *request;
@@ -94,8 +95,14 @@ 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_ready(DnsQueryCandidate *c);
+void dns_query_candidate_notify(DnsQueryCandidate *c);
 
 int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
 DnsQuery *dns_query_free(DnsQuery *q);
index 3249448d3b73ba145badcabd850c41c6beba8002..4ed7434d3c50865edd133fe15cdb2b871b571894 100644 (file)
@@ -117,7 +117,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char
                 return 0;
 
         for (i = 0; i < q->n_keys; i++) {
-                r = dns_resource_key_match_cname(q->keys[i], rr, search_domain);
+                r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
                 if (r != 0)
                         return r;
         }
index 934a18334c85e9164e0353b2f2c35556e7e56349..dbf840157f27530aeaca893f889ab49ba34c0ce0 100644 (file)
@@ -168,6 +168,9 @@ bool dns_resource_key_is_address(const DnsResourceKey *key) {
 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
         int r;
 
+        if (a == b)
+                return 1;
+
         r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
         if (r <= 0)
                 return r;
@@ -181,12 +184,15 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
         return 1;
 }
 
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
+int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
         int r;
 
         assert(key);
         assert(rr);
 
+        if (key == rr->key)
+                return 1;
+
         /* Checks if an rr matches the specified key. If a search
          * domain is specified, it will also be checked if the key
          * with the search domain suffixed might match the RR. */
@@ -214,19 +220,19 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord
         return 0;
 }
 
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) {
+int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
         int r;
 
         assert(key);
-        assert(rr);
+        assert(cname);
 
-        if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
+        if (cname->class != key->class && key->class != DNS_CLASS_ANY)
                 return 0;
 
-        if (rr->key->type == DNS_TYPE_CNAME)
-                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
-        else if (rr->key->type == DNS_TYPE_DNAME)
-                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
+        if (cname->type == DNS_TYPE_CNAME)
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
+        else if (cname->type == DNS_TYPE_DNAME)
+                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname));
         else
                 return 0;
 
@@ -240,14 +246,28 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec
                 if (r < 0)
                         return r;
 
-                if (rr->key->type == DNS_TYPE_CNAME)
-                        return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(rr->key));
-                else if (rr->key->type == DNS_TYPE_DNAME)
-                        return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(rr->key));
+                if (cname->type == DNS_TYPE_CNAME)
+                        return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(cname));
+                else if (cname->type == DNS_TYPE_DNAME)
+                        return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(cname));
         }
 
         return 0;
+}
+
+int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa) {
+        assert(soa);
+        assert(key);
 
+        /* Checks whether 'soa' is a SOA record for the specified key. */
+
+        if (soa->class != key->class)
+                return 0;
+
+        if (soa->type != DNS_TYPE_SOA)
+                return 0;
+
+        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) {
@@ -288,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);
@@ -303,7 +326,8 @@ 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;
@@ -319,6 +343,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
 
         rr->n_ref = 1;
         rr->key = dns_resource_key_ref(key);
+        rr->expiry = USEC_INFINITY;
 
         return rr;
 }
@@ -428,6 +453,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
                 dns_resource_key_unref(rr->key);
         }
 
+        free(rr->to_string);
         free(rr);
 
         return NULL;
@@ -503,6 +529,9 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
         assert(a);
         assert(b);
 
+        if (a == b)
+                return 1;
+
         r = dns_resource_key_equal(a->key, b->key);
         if (r <= 0)
                 return r;
@@ -740,16 +769,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) {
 
@@ -761,7 +793,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:
@@ -770,25 +802,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: {
@@ -796,22 +828,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:
@@ -825,7 +857,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:
@@ -834,7 +866,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:
@@ -847,17 +879,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,
@@ -866,13 +898,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,
@@ -880,58 +912,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,
@@ -940,21 +976,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: {
@@ -963,16 +999,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,
@@ -983,7 +1019,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
                              hash,
                              t);
                 if (r < 0)
-                        return -ENOMEM;
+                        return NULL;
 
                 break;
         }
@@ -991,16 +1027,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) {
@@ -1048,34 +1085,157 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
         return 0;
 }
 
-const char *dns_class_to_string(uint16_t class) {
+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;
 
-        switch (class) {
+        case DNS_TYPE_TXT:
+        case DNS_TYPE_SPF: {
+                DnsTxtItem *j;
 
-        case DNS_CLASS_IN:
-                return "IN";
+                LIST_FOREACH(items, j, rr->txt.items) {
+                        siphash24_compress(j->data, j->length, state);
 
-        case DNS_CLASS_ANY:
-                return "ANY";
+                        /* 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;
         }
 
-        return NULL;
+        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;
+        }
 }
 
-int dns_class_from_string(const char *s, uint16_t *class) {
-        assert(s);
-        assert(class);
+static int dns_resource_record_compare_func(const void *a, const void *b) {
+        const DnsResourceRecord *x = a, *y = b;
+        int ret;
 
-        if (strcaseeq(s, "IN"))
-                *class = DNS_CLASS_IN;
-        else if (strcaseeq(s, "ANY"))
-                *class = DNS_CLASS_ANY;
-        else
-                return -EINVAL;
+        ret = dns_resource_key_compare_func(x->key, y->key);
+        if (ret != 0)
+                return ret;
 
-        return 0;
+        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;
 
@@ -1090,6 +1250,9 @@ DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
 
 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
 
+        if (a == b)
+                return true;
+
         if (!a != !b)
                 return false;
 
@@ -1106,6 +1269,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",
@@ -1115,14 +1279,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 5c2306ba96ae97b18f3510819da017e93b6e85f4..fe29a4156693eaf0a3b113372392ab14ea9e37fc 100644 (file)
@@ -33,17 +33,10 @@ typedef struct DnsResourceKey DnsResourceKey;
 typedef struct DnsResourceRecord DnsResourceRecord;
 typedef struct DnsTxtItem DnsTxtItem;
 
-/* DNS record classes, see RFC 1035 */
-enum {
-        DNS_CLASS_IN   = 0x01,
-        DNS_CLASS_ANY  = 0xFF,
-        _DNS_CLASS_MAX,
-        _DNS_CLASS_INVALID = -1
-};
-
 /* 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)
@@ -59,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,
@@ -71,15 +67,23 @@ 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;
         uint16_t class, type;
         char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
-        bool cache_flush:1;
 };
 
 /* Creates a temporary resource key. This is only useful to quickly
@@ -104,7 +108,9 @@ struct DnsTxtItem {
 struct DnsResourceRecord {
         unsigned n_ref;
         DnsResourceKey *key;
+        char *to_string;
         uint32_t ttl;
+        usec_t expiry; /* RRSIG signature expiry */
         bool unparseable:1;
         bool wire_format_canonical:1;
         void *wire_format;
@@ -114,7 +120,7 @@ struct DnsResourceRecord {
                 struct {
                         void *data;
                         size_t size;
-                } generic;
+                } generic, opt;
 
                 struct {
                         uint16_t priority;
@@ -159,6 +165,7 @@ struct DnsResourceRecord {
                         char *exchange;
                 } mx;
 
+                /* https://tools.ietf.org/html/rfc1876 */
                 struct {
                         uint8_t version;
                         uint8_t size;
@@ -169,14 +176,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;
@@ -214,6 +213,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;
@@ -228,7 +236,7 @@ struct DnsResourceRecord {
 };
 
 static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
-        if (_unlikely_(!key))
+        if (!key)
                 return NULL;
 
         if (key->_name)
@@ -237,6 +245,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);
@@ -245,8 +274,9 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
 DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
 bool dns_resource_key_is_address(const DnsResourceKey *key);
 int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
-int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
-int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
+int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain);
+int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain);
+int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa);
 int dns_resource_key_to_string(const DnsResourceKey *key, char **ret);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref);
 
@@ -261,7 +291,7 @@ 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);
@@ -269,13 +299,11 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
 
-const char *dns_class_to_string(uint16_t type);
-int dns_class_from_string(const char *name, uint16_t *class);
-
 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 eae903526b88f6489d7a353874f2894d94b6f2a2..dd3609bd127b6c24aefbbf0fda396107bab50dc7 100644 (file)
@@ -57,6 +57,23 @@ 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 = l->dnssec_mode;
+                if (s->dnssec_mode == _DNSSEC_MODE_INVALID)
+                        s->dnssec_mode = m->dnssec_mode;
+                if (s->dnssec_mode == _DNSSEC_MODE_INVALID)
+                        s->dnssec_mode = DNSSEC_NO;
+        }
+
         LIST_PREPEND(scopes, m->dns_scopes, s);
 
         dns_scope_llmnr_membership(s, true);
@@ -81,7 +98,8 @@ static void dns_scope_abort_transactions(DnsScope *s) {
                  * freed while we still look at it */
 
                 t->block_gc++;
-                dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
+                if (DNS_TRANSACTION_IS_LIVE(t->state))
+                        dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
                 t->block_gc--;
 
                 dns_transaction_free(t);
@@ -161,17 +179,15 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
                 s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
 }
 
-int dns_scope_emit(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;
@@ -180,30 +196,13 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                 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;
 
@@ -214,15 +213,11 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                 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;
 
@@ -249,6 +244,8 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
                 break;
 
         case DNS_PROTOCOL_MDNS:
+                assert(fd < 0);
+
                 if (!ratelimit_test(&s->ratelimit))
                         return -EBUSY;
 
@@ -278,8 +275,39 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
         return 1;
 }
 
-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;
+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));
+
+        do {
+                /* If there are multiple linked packets, set the TC bit in all but the last of them */
+                if (p->more) {
+                        assert(p->protocol == DNS_PROTOCOL_MDNS);
+                        dns_packet_set_flags(p, true, true);
+                }
+
+                r = dns_scope_emit_one(s, fd, p);
+                if (r < 0)
+                        return r;
+
+                p = p->more;
+        } while (p);
+
+        return 0;
+}
+
+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;
@@ -287,31 +315,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) {
@@ -369,21 +393,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) {
@@ -416,6 +437,10 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
             dns_name_equal(domain, "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.0.ip6.arpa") > 0)
                 return DNS_SCOPE_NO;
 
+        /* Never respond to some of the domains listed in RFC6761 */
+        if (dns_name_endswith(domain, "invalid") > 0)
+                return DNS_SCOPE_NO;
+
         /* Always honour search domains for routing queries. Note that
          * we return DNS_SCOPE_YES here, rather than just
          * DNS_SCOPE_MAYBE, which means wildcard scopes won't be
@@ -433,7 +458,11 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
                     dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 &&
                     dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 &&
                     dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 &&
-                    dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0)
+                    dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0 &&
+                    /* If networks use .local in their private setups, they are supposed to also add .local to their search
+                     * domains, which we already checked above. Otherwise, we consider .local specific to mDNS and won't
+                     * send such queries ordinary DNS servers. */
+                    dns_name_endswith(domain, "local") == 0)
                         return DNS_SCOPE_MAYBE;
 
                 return DNS_SCOPE_NO;
@@ -473,7 +502,7 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
 
         if (s->protocol == DNS_PROTOCOL_DNS) {
 
-                /* On classic DNS, lookin up non-address RRs is always
+                /* On classic DNS, looking up non-address RRs is always
                  * fine. (Specifically, we want to permit looking up
                  * DNSKEY and DS records on the root and top-level
                  * domains.) */
@@ -756,7 +785,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key,
         /* Refuse reusing transactions that completed based on cached
          * data instead of a real packet, if that's requested. */
         if (!cache_ok &&
-            IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
+            IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_RCODE_FAILURE) &&
             t->answer_source != DNS_TRANSACTION_NETWORK)
                 return NULL;
 
@@ -789,7 +818,11 @@ static int dns_scope_make_conflict_packet(
                                                               0 /* (ad) */,
                                                               0 /* (cd) */,
                                                               0));
-        random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
+
+        /* For mDNS, the transaction ID should always be 0 */
+        if (s->protocol != DNS_PROTOCOL_MDNS)
+                random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
+
         DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
         DNS_PACKET_HEADER(p)->arcount = htobe16(1);
 
@@ -830,7 +863,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");
         }
@@ -879,6 +912,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..0969e31e8a0e483f54f79d1fa7562e7e64aac85c 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,66 @@ 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 feature level %s.", dns_server_feature_level_to_string(level));
+                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 (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
+                        /* Even if we successfully receive a reply to a request announcing support for large packets,
+                           that does not mean we can necessarily receive large packets. */
+                        dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_LARGE - 1);
+                else
+                        /* A successful UDP reply, verifies UDP, ENDS0 and DO levels */
+                        dns_server_verified(s, level);
+
+        } 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. */
+                dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_TCP);
+        }
 
         /* 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 +292,37 @@ 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->n_failed_attempts  = (unsigned) -1;
+        s->packet_failed = true;
+}
+
+void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
+        assert(s);
+        assert(s->manager);
+
+        /* Invoked whenever we get a packet with TC bit set. */
+
+        if (s->possible_feature_level != level)
+                return;
+
+        s->packet_truncated = true;
+}
+
+void dns_server_packet_rrsig_missing(DnsServer *s) {
+        assert(s);
+        assert(s->manager);
+
+        log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", dns_server_string(s));
+
+        s->rrsig_missing = true;
 }
 
 static bool dns_server_grace_period_expired(DnsServer *s) {
@@ -297,35 +344,138 @@ 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);
 
-        if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
+        s->n_failed_udp = 0;
+        s->n_failed_tcp = 0;
+        s->packet_failed = false;
+        s->packet_truncated = false;
+        s->verified_usec = 0;
+}
+
+DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
+        assert(s);
+
+        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;
+                s->rrsig_missing = false;
+
+                dns_server_reset_counters(s);
+
+                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. */
+                        s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
+
+                else if ((s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+                          s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) ||
+                         (s->packet_failed &&
+                          s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) ||
+                         (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+                          s->packet_truncated &&
+                          s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP))
+
+                        /* Downgrade the feature one level, maybe things will work better then. We do this under any of
+                         * three conditions:
+                         *
+                         * 1. We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If
+                         *    the packets are lost, maybe the server cannot parse them, hence downgrading sounds like a
+                         *    good idea. We might downgrade all the way down to TCP this way.
+                         *
+                         * 2. We got a failure packet, and are at a feature level above UDP. Note that in this case we
+                         *    downgrade no further than UDP, under the assumption that a failure packet indicates an
+                         *    incompatible packet contents, but not a problem with the transport.
+                         *
+                         * 3. We got too many TCP connection failures in a row, we had at least one truncated packet,
+                         *    and are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or
+                         *    EDNS0 data we hope to make the packet smaller, so that it still works via UDP given that
+                         *    TCP appears not to be a fallback. Note that if we are already at the lowest UDP level, we
+                         *    don't go further down, since that's TCP, and TCP failed too often after all.
+                         */
+
+                        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->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 +569,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..323f702903dced8243ccfff6a665b5b90cee5c6b 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;
         usec_t verified_usec;
         usec_t features_grace_period_usec;
 
+        /* Indicates whether responses are augmented with RRSIG by
+         * server or not. Note that this is orthogonal to the feature
+         * level stuff, as it's only information describing responses,
+         * and has no effect on how the questions are asked. */
+        bool rrsig_missing:1;
+
+        /* 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,19 @@ 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 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 +132,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 90f07e6c4b9f6a6fb773c58f7b3398c42dfe8342..9ee10f21c87ba6ff2906aa2913ec1b604cc0bc4e 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"
 
+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_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;
+        DnsTransaction *z;
 
         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);
@@ -57,16 +83,27 @@ 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->query_candidates)))
+        while ((c = set_steal_first(t->notify_query_candidates)))
                 set_remove(c->transactions, t);
+        set_free(t->notify_query_candidates);
 
-        set_free(t->query_candidates);
-
-        while ((i = set_steal_first(t->zone_items)))
+        while ((i = set_steal_first(t->notify_zone_items)))
                 i->probe_transaction = NULL;
-        set_free(t->zone_items);
+        set_free(t->notify_zone_items);
+
+        while ((z = set_steal_first(t->notify_transactions)))
+                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);
+        }
+        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;
@@ -74,14 +111,20 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
 
 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->query_candidates) && set_isempty(t->zone_items))
+        if (set_isempty(t->notify_query_candidates) &&
+            set_isempty(t->notify_zone_items) &&
+            set_isempty(t->notify_transactions)) {
                 dns_transaction_free(t);
+                return false;
+        }
+
+        return true;
 }
 
 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
@@ -92,6 +135,16 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         assert(s);
         assert(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;
+
         r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
         if (r < 0)
                 return r;
@@ -106,7 +159,10 @@ 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
@@ -129,6 +185,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;
 
@@ -137,16 +195,6 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
         return 0;
 }
 
-static void dns_transaction_stop(DnsTransaction *t) {
-        assert(t);
-
-        t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
-        t->stream = dns_stream_free(t->stream);
-
-        /* Note that we do not drop the UDP socket here, as we want to
-         * reuse it to repeat the interaction. */
-}
-
 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         _cleanup_free_ char *pretty = NULL;
         DnsZoneItem *z;
@@ -159,7 +207,9 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
 
         in_addr_to_string(p->family, &p->sender, &pretty);
 
-        log_debug("Transaction on scope %s on %s/%s got tentative packet from %s",
+        log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
+                  t->id,
+                  dns_transaction_key_string(t),
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
@@ -175,7 +225,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
 
         t->block_gc++;
-        while ((z = set_first(t->zone_items))) {
+        while ((z = set_first(t->notify_zone_items))) {
                 /* First, make sure the zone item drops the reference
                  * to us */
                 dns_zone_item_probe_stop(z);
@@ -192,38 +242,118 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
 void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
         DnsQueryCandidate *c;
         DnsZoneItem *z;
+        DnsTransaction *d;
         Iterator i;
 
         assert(t);
-        assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+        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. */
 
-        log_debug("Transaction on scope %s on %s/%s now complete with <%s> from %s",
+        log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
+                  t->id,
+                  dns_transaction_key_string(t),
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
                   dns_transaction_state_to_string(state),
-                  t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source));
+                  t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
+                  t->answer_authenticated ? "authenticated" : "unsigned");
 
         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 */
         t->block_gc++;
-        SET_FOREACH(c, t->query_candidates, i)
-                dns_query_candidate_ready(c);
-        SET_FOREACH(z, t->zone_items, i)
-                dns_zone_item_ready(z);
-        t->block_gc--;
 
+        SET_FOREACH(c, t->notify_query_candidates, i)
+                dns_query_candidate_notify(c);
+        SET_FOREACH(z, t->notify_zone_items, i)
+                dns_zone_item_notify(z);
+
+        if (!set_isempty(t->notify_transactions)) {
+                DnsTransaction **nt;
+                unsigned j, n = 0;
+
+                /* We need to be careful when notifying other
+                 * transactions, as that might destroy other
+                 * transactions in our list. Hence, in order to be
+                 * able to safely iterate through the list of
+                 * transactions, take a GC lock on all of them
+                 * first. Then, in a second loop, notify them, but
+                 * first unlock that specific transaction. */
+
+                nt = newa(DnsTransaction*, set_size(t->notify_transactions));
+                SET_FOREACH(d, t->notify_transactions, i) {
+                        nt[n++] = d;
+                        d->block_gc++;
+                }
+
+                assert(n == set_size(t->notify_transactions));
+
+                for (j = 0; j < n; j++) {
+                        if (set_contains(t->notify_transactions, nt[j]))
+                                dns_transaction_notify(nt[j], t);
+
+                        nt[j]->block_gc--;
+                        dns_transaction_gc(nt[j]);
+                }
+        }
+
+        t->block_gc--;
         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 on_stream_complete(DnsStream *s, int error) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
         DnsTransaction *t;
@@ -238,6 +368,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;
@@ -255,32 +395,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;
@@ -297,7 +451,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;
@@ -312,7 +466,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);
@@ -321,11 +474,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;
 
@@ -335,17 +483,92 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
         if (t->scope->link)
                 t->stream->ifindex = t->scope->link->ifindex;
 
+        dns_transaction_reset_answer(t);
+
+        t->tried_stream = true;
+
         return 0;
 }
 
-static void dns_transaction_next_dns_server(DnsTransaction *t) {
+static void dns_transaction_cache_answer(DnsTransaction *t) {
         assert(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);
+        /* For mDNS we cache whenever we get the packet, rather than
+         * in each transaction. */
+        if (!IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR))
+                return;
 
-        dns_scope_next_dns_server(t->scope);
+        /* We never cache if this packet is from the local host, under
+         * the assumption that a locally running DNS server would
+         * cache this anyway, and probably knows better when to flush
+         * the cache then we could. */
+        if (!DNS_PACKET_SHALL_CACHE(t->received))
+                return;
+
+        dns_cache_put(&t->scope->cache,
+                      t->key,
+                      t->answer_rcode,
+                      t->answer,
+                      t->answer_authenticated,
+                      t->answer_nsec_ttl,
+                      0,
+                      t->received->family,
+                      &t->received->sender);
+}
+
+static bool dns_transaction_dnssec_is_live(DnsTransaction *t) {
+        DnsTransaction *dt;
+        Iterator i;
+
+        assert(t);
+
+        SET_FOREACH(dt, t->dnssec_transactions, i)
+                if (DNS_TRANSACTION_IS_LIVE(dt->state))
+                        return true;
+
+        return false;
+}
+
+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))
+                return;
+
+        /* All our auxiliary DNSSEC transactions are complete now. Try
+         * to validate our RRset now. */
+        r = dns_transaction_validate_dnssec(t);
+        if (r < 0) {
+                dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                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_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
+                dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
+                return;
+        }
+
+        dns_transaction_cache_answer(t);
+
+        if (t->answer_rcode == DNS_RCODE_SUCCESS)
+                dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+        else
+                dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
 }
 
 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
@@ -354,15 +577,20 @@ 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. */
 
+        log_debug("Processing incoming packet on transaction %" PRIu16".", t->id);
+
         switch (t->scope->protocol) {
+
         case DNS_PROTOCOL_LLMNR:
                 assert(t->scope->link);
 
@@ -398,6 +626,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:
@@ -428,34 +661,32 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
 
         switch (t->scope->protocol) {
+
         case DNS_PROTOCOL_DNS:
                 assert(t->server);
 
                 if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) {
 
-                        /* request failed, immediately try again with reduced features */
+                        /* 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);
+                else
+                        dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, ts - t->start_usec, p->size);
 
                 break;
+
         case DNS_PROTOCOL_LLMNR:
         case DNS_PROTOCOL_MDNS:
                 dns_scope_packet_received(t->scope, ts - t->start_usec);
-
                 break;
+
         default:
-                break;
+                assert_not_reached("Invalid DNS protocol.");
         }
 
         if (DNS_PACKET_TC(p)) {
@@ -466,6 +697,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) {
@@ -473,37 +706,42 @@ 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 and mDNS, if we cannot connect to the host,
+                        /* 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 and update the cache */
+        /* Parse message, if it isn't parsed yet. */
         r = dns_packet_extract(p);
         if (r < 0) {
                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
                 return;
         }
 
-        if (t->scope->protocol == DNS_PROTOCOL_DNS) {
+        if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) {
+
                 /* Only consider responses with equivalent query section to the request */
-                if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
+                r = dns_packet_is_reply_for(p, t->key);
+                if (r < 0) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                        return;
+                }
+                if (r == 0) {
                         dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
                         return;
                 }
@@ -512,25 +750,39 @@ 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_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p);
-
-                /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
-                if (DNS_PACKET_SHALL_CACHE(p))
-                        dns_cache_put(&t->scope->cache,
-                                      t->key,
-                                      DNS_PACKET_RCODE(p),
-                                      p->answer,
-                                      DNS_PACKET_ANCOUNT(p),
-                                      t->answer_authenticated,
-                                      0,
-                                      p->family,
-                                      &p->sender);
-        }
-
-        if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
-                dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
-        else
-                dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+                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;
+                }
+                if (r > 0) {
+                        /* There are DNSSEC transactions pending now. Update the state accordingly. */
+                        t->state = DNS_TRANSACTION_VALIDATING;
+                        dns_transaction_close_connection(t);
+                        dns_transaction_stop_timeout(t);
+                        return;
+                }
+        }
+
+        dns_transaction_process_dnssec(t);
 }
 
 static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -542,53 +794,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.");
+                /* 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: %m");
+                return 0;
+        }
+
+        if (DNS_PACKET_ID(p) != t->id) {
+                log_debug("Received packet with incorrect transaction ID, ignoring: %m");
+                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);
+
+                        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(t->scope, t->dns_udp_fd, t->server, t->sent);
+        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);
@@ -596,17 +891,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.");
                 }
@@ -615,13 +910,9 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
                         t->initial_jitter_elapsed = true;
         }
 
-        /* ...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);
+        log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
 
+        dns_transaction_retry(t);
         return 0;
 }
 
@@ -630,36 +921,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_next_attempt(DnsTransaction *t, usec_t ts) {
-        bool had_stream;
+static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
         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);
@@ -668,14 +959,12 @@ static int dns_transaction_prepare_next_attempt(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);
 
         /* 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) {
@@ -685,11 +974,46 @@ static int dns_transaction_prepare_next_attempt(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
          * for probing or verifying a zone item. */
-        if (set_isempty(t->zone_items)) {
+        if (set_isempty(t->notify_zone_items)) {
 
                 r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL);
                 if (r < 0)
@@ -705,7 +1029,7 @@ static int dns_transaction_prepare_next_attempt(DnsTransaction *t, usec_t ts) {
 
         /* Check the cache, but only if this transaction is not used
          * for probing or verifying a zone item. */
-        if (set_isempty(t->zone_items)) {
+        if (set_isempty(t->notify_zone_items)) {
 
                 /* Before trying the cache, let's make sure we figured out a
                  * server to use. Should this cause a change of server this
@@ -723,7 +1047,7 @@ static int dns_transaction_prepare_next_attempt(DnsTransaction *t, usec_t ts) {
                         if (t->answer_rcode == DNS_RCODE_SUCCESS)
                                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
                         else
-                                dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+                                dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
                         return 0;
                 }
         }
@@ -743,7 +1067,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         assert(t);
         assert(t->scope->protocol == DNS_PROTOCOL_MDNS);
 
-        /* Discard any previously prepared packet, so we can start over and coaleasce again */
+        /* Discard any previously prepared packet, so we can start over and coalesce again */
         t->sent = dns_packet_unref(t->sent);
 
         r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
@@ -794,7 +1118,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
                 if (r < 0)
                         return r;
 
-                r = dns_transaction_prepare_next_attempt(other, ts);
+                r = dns_transaction_prepare(other, ts);
                 if (r <= 0)
                         continue;
 
@@ -809,6 +1133,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;
 
@@ -819,7 +1145,6 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         }
 
         DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
-        DNS_PACKET_HEADER(p)->id = t->id;
 
         /* Append known answer section if we're asking for any shared record */
         if (add_known_answers) {
@@ -846,7 +1171,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;
 
@@ -876,11 +1201,14 @@ int dns_transaction_go(DnsTransaction *t) {
         assert(t);
 
         assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
-        r = dns_transaction_prepare_next_attempt(t, ts);
+
+        r = dns_transaction_prepare(t, ts);
         if (r <= 0)
                 return r;
 
-        log_debug("Excercising transaction on scope %s on %s/%s",
+        log_debug("Excercising transaction %" PRIu16 " for <%s> on scope %s on %s/%s.",
+                  t->id,
+                  dns_transaction_key_string(t),
                   dns_protocol_to_string(t->scope->protocol),
                   t->scope->link ? t->scope->link->name : "*",
                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
@@ -898,10 +1226,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;
@@ -920,6 +1250,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;
@@ -950,7 +1282,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);
         }
@@ -959,14 +1295,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);
         }
@@ -982,16 +1324,1502 @@ 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;
+
+        assert(t);
+        assert(ret);
+        assert(key);
+
+        aux = dns_scope_find_transaction(t->scope, key, true);
+        if (!aux) {
+                r = dns_transaction_new(&aux, t->scope, key);
+                if (r < 0)
+                        return r;
+        } else {
+                if (set_contains(t->dnssec_transactions, aux)) {
+                        *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);
+        if (r < 0)
+                goto gc;
+
+        r = set_ensure_allocated(&aux->notify_transactions, NULL);
+        if (r < 0)
+                goto gc;
+
+        r = set_put(t->dnssec_transactions, aux);
+        if (r < 0)
+                goto gc;
+
+        r = set_put(aux->notify_transactions, t);
+        if (r < 0) {
+                (void) set_remove(t->dnssec_transactions, aux);
+                goto gc;
+        }
+
+        *ret = aux;
+        return 1;
+
+gc:
+        dns_transaction_gc(aux);
+        return r;
+}
+
+static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
+        DnsTransaction *aux;
+        int r;
+
+        assert(t);
+        assert(key);
+
+        /* Try to get the data from the trust anchor */
+        r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                r = dns_answer_extend(&t->validated_keys, a);
+                if (r < 0)
+                        return r;
+
+                return 0;
+        }
+
+        /* 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;
+
+        if (aux->state == DNS_TRANSACTION_NULL) {
+                r = dns_transaction_go(aux);
+                if (r < 0)
+                        return r;
+        }
+
+        return 1;
+}
+
+static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
+        int r;
+
+        assert(t);
+
+        /* Checks whether the answer is positive, i.e. either a direct
+         * answer to the question, or a CNAME/DNAME for it */
+
+        r = dns_answer_match_key(t->answer, t->key, flags);
+        if (r != 0)
+                return r;
+
+        r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
+        if (r != 0)
+                return r;
+
+        return false;
+}
+
+static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
+        int r;
+
+        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;
+
+        assert(t);
+
+        /* Checks whether the answer is negative, and lacks NSEC/NSEC3
+         * RRs to prove it */
+
+        r = dns_transaction_has_positive_answer(t, NULL);
+        if (r < 0)
+                return r;
+        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. */
+
+        r = dns_answer_contains_nsec_or_nsec3(t->answer);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        return true;
+}
+
+static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRecord *rr) {
+        int r;
+
+        assert(t);
+        assert(rr);
+
+        /* Check if the specified RR is the "primary" response,
+         * i.e. either matches the question precisely or is a
+         * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */
+
+        r = dns_resource_key_match_rr(t->key, rr, NULL);
+        if (r != 0)
+                return r;
+
+        r = dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
+        if (r != 0)
+                return r;
+
+        if (rr->key->type == DNS_TYPE_NSEC3) {
+                const char *p;
+
+                p = DNS_RESOURCE_KEY_NAME(rr->key);
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), p);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                return true;
+                }
+        }
+
+        return rr->key->type == DNS_TYPE_NSEC;
+}
+
+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;
+
+        int r;
+
+        assert(t);
+
+        /*
+         * Retrieve all auxiliary RRs for the answer we got, so that
+         * we can verify signatures or prove that RRs are rightfully
+         * unsigned. Specifically:
+         *
+         * - 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/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_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: {
+                        /* For each RRSIG we request the matching DNSKEY */
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dnskey = NULL;
+
+                        /* If this RRSIG is about a DNSKEY RR and the
+                         * signer is the same as the owner, then we
+                         * already have the DNSKEY, and we don't have
+                         * to look for more. */
+                        if (rr->rrsig.type_covered == DNS_TYPE_DNSKEY) {
+                                r = dns_name_equal(rr->rrsig.signer, DNS_RESOURCE_KEY_NAME(rr->key));
+                                if (r < 0)
+                                        return r;
+                                if (r > 0)
+                                        continue;
+                        }
+
+                        /* If the signer is not a parent of our
+                         * original query, then this is about an
+                         * auxiliary RRset, but not anything we asked
+                         * for. In this case we aren't interested,
+                         * because we don't want to request additional
+                         * RRs for stuff we didn't really ask for, and
+                         * also to avoid request loops, where
+                         * additional RRs from one transaction result
+                         * in another transaction whose additonal RRs
+                         * point back to the original transaction, and
+                         * we deadlock. */
+                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), rr->rrsig.signer);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        dnskey = dns_resource_key_new(rr->key->class, DNS_TYPE_DNSKEY, rr->rrsig.signer);
+                        if (!dnskey)
+                                return -ENOMEM;
+
+                        log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), rr->rrsig.key_tag);
+                        r = dns_transaction_request_dnssec_rr(t, dnskey);
+                        if (r < 0)
+                                return r;
+                        break;
+                }
+
+                case DNS_TYPE_DNSKEY: {
+                        /* For each DNSKEY we request the matching DS */
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+                        /* If the DNSKEY we are looking at is not for
+                         * zone we are interested in, nor any of its
+                         * parents, we aren't interested, and don't
+                         * request it. After all, we don't want to end
+                         * up in request loops, and want to keep
+                         * additional traffic down. */
+
+                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), DNS_RESOURCE_KEY_NAME(rr->key));
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key));
+                        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, false));
+                        r = dns_transaction_request_dnssec_rr(t, ds);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+
+                case DNS_TYPE_SOA:
+                case DNS_TYPE_NS: {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
+
+                        /* For an unsigned SOA or NS, try to acquire
+                         * the matching DS RR, as we are at a zone cut
+                         * then, and whether a DS exists tells us
+                         * whether the zone is signed. Do so only if
+                         * this RR matches our original question,
+                         * however. */
+
+                        r = dns_resource_key_match_rr(t->key, rr, NULL);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        r = dnssec_has_rrsig(t->answer, rr->key);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
+                        ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key));
+                        if (!ds)
+                                return -ENOMEM;
+
+                        log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
+                        r = dns_transaction_request_dnssec_rr(t, ds);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+
+                case DNS_TYPE_DS:
+                case DNS_TYPE_CNAME:
+                case DNS_TYPE_DNAME: {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+                        const char *name;
+
+                        /* CNAMEs and DNAMEs cannot be located at a
+                         * zone apex, hence ask for the parent SOA for
+                         * unsigned CNAME/DNAME RRs, maybe that's the
+                         * apex. But do all that only if this is
+                         * actually a response to our original
+                         * 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)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        r = dnssec_has_rrsig(t->answer, rr->key);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
+                        name = DNS_RESOURCE_KEY_NAME(rr->key);
+                        r = dns_name_parent(&name);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, name);
+                        if (!soa)
+                                return -ENOMEM;
+
+                        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;
+
+                        break;
+                }
+
+                default: {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+
+                        /* 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);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        r = dnssec_has_rrsig(t->answer, rr->key);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
+                        soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, DNS_RESOURCE_KEY_NAME(rr->key));
+                        if (!soa)
+                                return -ENOMEM;
+
+                        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;
+                        break;
+                }}
+        }
+
+        /* Above, we requested everything necessary to validate what
+         * we got. Now, let's request what we need to validate what we
+         * didn't get... */
+
+        r = dns_transaction_has_unsigned_negative_answer(t);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                const char *name;
+
+                name = DNS_RESOURCE_KEY_NAME(t->key);
+
+                /* If this was a SOA or NS request, then this
+                 * indicates that we are not at a zone apex, hence ask
+                 * the parent name instead. If this was a DS request,
+                 * then it's signed when the parent zone is signed,
+                 * hence ask the parent in that case, too. */
+
+                if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+                        r = dns_name_parent(&name);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
+                        else
+                                name = NULL;
+                } else
+                        log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
+
+                if (name) {
+                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
+
+                        soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name);
+                        if (!soa)
+                                return -ENOMEM;
+
+                        r = dns_transaction_request_dnssec_rr(t, soa);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return dns_transaction_dnssec_is_live(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 */
+
+        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);
+}
+
+static int dns_transaction_validate_dnskey_by_ds(DnsTransaction *t) {
+        DnsResourceRecord *rr;
+        int ifindex, r;
+
+        assert(t);
+
+        /* Add all DNSKEY RRs from the answer that are validated by DS
+         * RRs from the list of validated keys to the list of
+         * validated keys. */
+
+        DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, t->answer) {
+
+                r = dnssec_verify_dnskey_search(rr, t->validated_keys);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                /* If so, the DNSKEY is validated too. */
+                r = dns_answer_add_extend(&t->validated_keys, rr, ifindex, DNS_ANSWER_AUTHENTICATED);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *rr) {
+        int r;
+
+        assert(t);
+        assert(rr);
+
+        /* 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_NO)
+                return false;
+
+        if (dns_type_is_pseudo(rr->key->type))
+                return -EINVAL;
+
+        r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return false;
+
+        switch (rr->key->type) {
+
+        case DNS_TYPE_RRSIG:
+                /* RRSIGs are the signatures themselves, they need no signing. */
+                return false;
+
+        case DNS_TYPE_SOA:
+        case DNS_TYPE_NS: {
+                DnsTransaction *dt;
+                Iterator i;
+
+                /* For SOA or NS RRs we look for a matching DS transaction */
+
+                SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+                        if (dt->key->class != rr->key->class)
+                                continue;
+                        if (dt->key->type != DNS_TYPE_DS)
+                                continue;
+
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key));
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        /* We found a DS transactions for the SOA/NS
+                         * RRs we are looking at. If it discovered signed DS
+                         * RRs, then we need to be signed, too. */
+
+                        if (!dt->answer_authenticated)
+                                return false;
+
+                        return dns_answer_match_key(dt->answer, dt->key, NULL);
+                }
+
+                /* We found nothing that proves this is safe to leave
+                 * this unauthenticated, hence ask inist on
+                 * authentication. */
+                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.
+                 *
+                 * DS RRs are signed if the parent is signed, hence also look at the parent SOA
+                 */
+
+                SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+                        if (dt->key->class != rr->key->class)
+                                continue;
+                        if (dt->key->type != DNS_TYPE_SOA)
+                                continue;
+
+                        if (!parent) {
+                                parent = DNS_RESOURCE_KEY_NAME(rr->key);
+                                r = dns_name_parent(&parent);
+                                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;
+                                }
+                        }
+
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), parent);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        return t->answer_authenticated;
+                }
+
+                return true;
+        }
+
+        default: {
+                DnsTransaction *dt;
+                Iterator i;
+
+                /* 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) {
+
+                        if (dt->key->class != rr->key->class)
+                                continue;
+                        if (dt->key->type != DNS_TYPE_SOA)
+                                continue;
+
+                        r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key));
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
+                        /* We found the transaction that was supposed to find
+                         * the SOA RR for us. It was successful, but found no
+                         * RR for us. This means we are not at a zone cut. In
+                         * this case, we require authentication if the SOA
+                         * lookup was authenticated too. */
+                        return t->answer_authenticated;
+                }
+
+                return true;
+        }}
+}
+
+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;
+        Iterator i;
+        int r;
+
+        assert(t);
+
+        /* Checks if we need to insist on NSEC/NSEC3 RRs for proving
+         * this negative reply */
+
+        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)) {
+
+                /* We got a negative reply for this SOA/NS lookup? If
+                 * so, then we are not at a zone apex, and thus should
+                 * look at the result of the parent SOA lookup.
+                 *
+                 * We got a negative reply for this DS lookup? DS RRs
+                 * are signed when their parent zone is signed, hence
+                 * also check the parent SOA in this case. */
+
+                r = dns_name_parent(&name);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return true;
+        }
+
+        /* For all other RRs we check the SOA on the same level to see
+         * if it's signed. */
+
+        SET_FOREACH(dt, t->dnssec_transactions, i) {
+
+                if (dt->key->class != t->key->class)
+                        continue;
+                if (dt->key->type != DNS_TYPE_SOA)
+                        continue;
+
+                r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), name);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                return dt->answer_authenticated;
+        }
+
+        /* If in doubt, require NSEC/NSEC3 */
+        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;
+}
+
+int dns_transaction_validate_dnssec(DnsTransaction *t) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL;
+        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;
+
+        assert(t);
+
+        /* We have now collected all DS and DNSKEY RRs in
+         * t->validated_keys, let's see which RRs we can now
+         * authenticate with that. */
+
+        if (t->scope->dnssec_mode == DNSSEC_NO)
+                return 0;
+
+        /* Already validated */
+        if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID)
+                return 0;
+
+        /* Our own stuff needs no validation */
+        if (IN_SET(t->answer_source, DNS_TRANSACTION_ZONE, DNS_TRANSACTION_TRUST_ANCHOR)) {
+                t->answer_dnssec_result = DNSSEC_VALIDATED;
+                t->answer_authenticated = true;
+                return 0;
+        }
+
+        /* Cached stuff is not affected by validation. */
+        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("Cannot validate 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 this response contains any revoked trust
+         * anchors we care about */
+        r = dns_transaction_check_revoked_trust_anchors(t);
+        if (r < 0)
+                return r;
+
+        /* Second, see if there are DNSKEYs we already know a
+         * validated DS for. */
+        r = dns_transaction_validate_dnskey_by_ds(t);
+        if (r < 0)
+                return r;
+
+        /* Third, remove all DNSKEY and DS RRs again that our trust
+         * 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, have_nsec = false;
+
+                DNS_ANSWER_FOREACH(rr, t->answer) {
+                        DnsResourceRecord *rrsig = NULL;
+                        DnssecResult result;
+
+                        switch (rr->key->type) {
+
+                        case DNS_TYPE_RRSIG:
+                                continue;
+
+                        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;
+
+                                break;
+
+                        default:
+                                /* We validate all other RRs only in the ALL phases */
+                                if (phase != PHASE_ALL)
+                                        continue;
+
+                                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) {
+                                        /* If we just validated a
+                                         * DNSKEY RRset, then let's
+                                         * add these keys to the set
+                                         * of validated keys for this
+                                         * transaction. */
+
+                                        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
+                                 * list of validated RRsets, and
+                                 * remove it from the unvalidated
+                                 * RRsets. We mark the RRset as
+                                 * authenticated and cacheable. */
+                                r = dns_answer_move_by_key(&validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE);
+                                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;
+                        }
+
+                        /* 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 *suffix;
+
+                                /* This RRset validated, but as a wildcard. This means we need to proof via NSEC/NSEC3
+                                 * that no matching non-wildcard RR exists.
+                                 *
+                                 * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4*/
+
+                                r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        return -EBADMSG;
+
+                                r = dns_name_parent(&suffix);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        return -EBADMSG;
+
+                                r = dnssec_nsec_test_between(validated, DNS_RESOURCE_KEY_NAME(rr->key), suffix, &authenticated);
+                                if (r < 0)
+                                        return r;
+
+                                /* 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 (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);
+
+                                        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_in_private_tld(t, rr->key);
+                                if (r < 0)
+                                        return r;
+                                if (r > 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;
+                                }
+                        }
+
+                        if (IN_SET(result,
+                                   DNSSEC_INVALID,
+                                   DNSSEC_SIGNATURE_EXPIRED,
+                                   DNSSEC_NO_SIGNATURE))
+                                t->scope->manager->n_dnssec_bogus++;
+                        else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+                                t->scope->manager->n_dnssec_indeterminate++;
+
+                        r = dns_transaction_is_primary_response(t, rr);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) {
+                                /* This is a primary response
+                                 * to our question, and it
+                                 * failed validation. That's
+                                 * fatal. */
+                                t->answer_dnssec_result = result;
+                                return 0;
+                        }
+
+                        /* 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 (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;
+                }
+
+                /* We're done */
+                break;
+        }
+
+        dns_answer_unref(t->answer);
+        t->answer = validated;
+        validated = NULL;
+
+        /* At this point the answer only contains validated
+         * RRsets. Now, let's see if it actually answers the question
+         * we asked. If so, great! If it doesn't, then see if
+         * NSEC/NSEC3 can prove this. */
+        r = dns_transaction_has_positive_answer(t, &flags);
+        if (r > 0) {
+                /* Yes, it answers the question! */
+
+                if (flags & DNS_ANSWER_AUTHENTICATED) {
+                        /* The answer is fully authenticated, yay. */
+                        t->answer_dnssec_result = DNSSEC_VALIDATED;
+                        t->answer_rcode = DNS_RCODE_SUCCESS;
+                        t->answer_authenticated = true;
+                } else {
+                        /* The answer is not fully authenticated. */
+                        t->answer_dnssec_result = DNSSEC_UNSIGNED;
+                        t->answer_authenticated = false;
+                }
+
+        } else if (r == 0) {
+                DnssecNsecResult nr;
+                bool authenticated = false;
+
+                /* Bummer! Let's check NSEC/NSEC3 */
+                r = dnssec_nsec_test(t->answer, t->key, &nr, &authenticated, &t->answer_nsec_ttl);
+                if (r < 0)
+                        return r;
+
+                switch (nr) {
+
+                case DNSSEC_NSEC_NXDOMAIN:
+                        /* NSEC proves the domain doesn't exist. Very good. */
+                        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 = authenticated;
+                        break;
+
+                case DNSSEC_NSEC_NODATA:
+                        /* NSEC proves that there's no data here, very good. */
+                        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 = authenticated;
+                        break;
+
+                case DNSSEC_NSEC_OPTOUT:
+                        /* NSEC3 says the data might not be signed */
+                        log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
+                        t->answer_dnssec_result = DNSSEC_UNSIGNED;
+                        t->answer_authenticated = false;
+                        break;
+
+                case DNSSEC_NSEC_NO_RR:
+                        /* No NSEC data? Bummer! */
+
+                        r = dns_transaction_requires_nsec(t);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
+                        else {
+                                t->answer_dnssec_result = DNSSEC_UNSIGNED;
+                                t->answer_authenticated = false;
+                        }
+
+                        break;
+
+                case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
+                        /* We don't know the NSEC3 algorithm used? */
+                        t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
+                        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;
+
+                default:
+                        assert_not_reached("Unexpected NSEC result.");
+                }
+        }
+
+        return 1;
+}
+
+const char *dns_transaction_key_string(DnsTransaction *t) {
+        assert(t);
+
+        if (!t->key_string) {
+                if (dns_resource_key_to_string(t->key, &t->key_string) < 0)
+                        return "n/a";
+        }
+
+        return strstrip(t->key_string);
+}
+
 static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
         [DNS_TRANSACTION_NULL] = "null",
         [DNS_TRANSACTION_PENDING] = "pending",
-        [DNS_TRANSACTION_FAILURE] = "failure",
+        [DNS_TRANSACTION_VALIDATING] = "validating",
+        [DNS_TRANSACTION_RCODE_FAILURE] = "rcode-failure",
         [DNS_TRANSACTION_SUCCESS] = "success",
         [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
         [DNS_TRANSACTION_TIMEOUT] = "timeout",
@@ -999,6 +2827,9 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
         [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
         [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 af08b20e44b381feef57143e3deeafbff242f69a..76cf6e71dba121b12af65dfdf3e379e06a57fc73 100644 (file)
@@ -28,7 +28,8 @@ typedef enum DnsTransactionSource DnsTransactionSource;
 enum DnsTransactionState {
         DNS_TRANSACTION_NULL,
         DNS_TRANSACTION_PENDING,
-        DNS_TRANSACTION_FAILURE,
+        DNS_TRANSACTION_VALIDATING,
+        DNS_TRANSACTION_RCODE_FAILURE,
         DNS_TRANSACTION_SUCCESS,
         DNS_TRANSACTION_NO_SERVERS,
         DNS_TRANSACTION_TIMEOUT,
@@ -36,10 +37,15 @@ enum DnsTransactionState {
         DNS_TRANSACTION_INVALID_REPLY,
         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
 };
 
+#define DNS_TRANSACTION_IS_LIVE(state) IN_SET((state), DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING)
+
 enum DnsTransactionSource {
         DNS_TRANSACTION_NETWORK,
         DNS_TRANSACTION_CACHE,
@@ -58,45 +64,75 @@ struct DnsTransaction {
         DnsScope *scope;
 
         DnsResourceKey *key;
+        char *key_string;
 
         DnsTransactionState state;
+
         uint16_t id;
 
-        bool initial_jitter_scheduled;
-        bool initial_jitter_elapsed;
+        bool tried_stream:1;
+
+        bool initial_jitter_scheduled:1;
+        bool initial_jitter_elapsed:1;
 
         DnsPacket *sent, *received;
 
         DnsAnswer *answer;
         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
+         * question are authenticated, or, if there are none, whether
+         * the NODATA or NXDOMAIN case is. It says nothing about
+         * additional RRs listed in the answer, however they have
+         * their own DNS_ANSWER_AUTHORIZED FLAGS. Note that this bit
+         * is defined different than the AD bit in DNS packets, as
+         * that covers more than just the actual primary answer. */
         bool answer_authenticated;
 
+        /* Contains DNSKEY, DS, SOA RRs we already verified and need
+         * to authenticate this reply */
+        DnsAnswer *validated_keys;
+
         usec_t start_usec;
         usec_t next_attempt_after;
         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;
+        /* The features of the DNS server at time of transaction start */
+        DnsServerFeatureLevel current_feature_level;
 
         /* Query candidates this transaction is referenced by and that
          * shall be notified about this specific transaction
          * completing. */
-        Set *query_candidates;
+        Set *notify_query_candidates;
 
         /* Zone items this transaction is referenced by and that shall
          * be notified about completion. */
-        Set *zone_items;
+        Set *notify_zone_items;
+
+        /* Other transactions that this transactions is referenced by
+         * and that shall be notified about completion. This is used
+         * when transactions want to validate their RRsets, but need
+         * another DNSKEY or DS RR to do so. */
+        Set *notify_transactions;
+
+        /* The opposite direction: the transactions this transaction
+         * created in order to request DNSKEY or DS RRs. */
+        Set *dnssec_transactions;
 
         unsigned block_gc;
 
@@ -106,12 +142,18 @@ 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);
 void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state);
 
+void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source);
+int dns_transaction_validate_dnssec(DnsTransaction *t);
+int dns_transaction_request_dnssec_keys(DnsTransaction *t);
+
+const char *dns_transaction_key_string(DnsTransaction *t);
+
 const char* dns_transaction_state_to_string(DnsTransactionState p) _const_;
 DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
 
index e55bdaa1ed63d57edd876c9b8b721f11131d1527..9bee44b5c7aff538460dade133f97ecba58d61ff 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 */
@@ -58,30 +88,445 @@ int dns_trust_anchor_load(DnsTrustAnchor *d) {
         if (!answer)
                 return -ENOMEM;
 
-        r = dns_answer_add(answer, rr, 0);
+        r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+        if (r < 0)
+                return r;
+
+        r = hashmap_put(d->positive_by_key, rr->key, answer);
         if (r < 0)
                 return r;
 
-        r = hashmap_put(d->by_key, rr->key, answer);
+        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(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 78f44d51a279ef0d7749f1b1cbad54380e7961d7..f60b0bddc16ba5adb600ad8aebfe9d5da7426588 100644 (file)
@@ -39,7 +39,7 @@ void dns_zone_item_probe_stop(DnsZoneItem *i) {
         t = i->probe_transaction;
         i->probe_transaction = NULL;
 
-        set_remove(t->zone_items, i);
+        set_remove(t->notify_zone_items, i);
         dns_transaction_gc(t);
 }
 
@@ -184,11 +184,11 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                         return r;
         }
 
-        r = set_ensure_allocated(&t->zone_items, NULL);
+        r = set_ensure_allocated(&t->notify_zone_items, NULL);
         if (r < 0)
                 goto gc;
 
-        r = set_put(t->zone_items, i);
+        r = set_put(t->notify_zone_items, i);
         if (r < 0)
                 goto gc;
 
@@ -206,7 +206,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                 }
         }
 
-        dns_zone_item_ready(i);
+        dns_zone_item_notify(i);
         return 0;
 
 gc:
@@ -223,9 +223,9 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
         assert(s);
         assert(rr);
 
-        if (rr->key->class == DNS_CLASS_ANY)
+        if (dns_class_is_pseudo(rr->key->class))
                 return -EINVAL;
-        if (rr->key->type == DNS_TYPE_ANY)
+        if (dns_type_is_pseudo(rr->key->type))
                 return -EINVAL;
 
         existing = dns_zone_get(z, rr);
@@ -386,7 +386,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                         if (k < 0)
                                 return k;
                         if (k > 0) {
-                                r = dns_answer_add(answer, j->rr, 0);
+                                r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
                                 if (r < 0)
                                         return r;
 
@@ -412,7 +412,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns
                         if (j->state != DNS_ZONE_ITEM_PROBING)
                                 tentative = false;
 
-                        r = dns_answer_add(answer, j->rr, 0);
+                        r = dns_answer_add(answer, j->rr, 0, DNS_ANSWER_AUTHENTICATED);
                         if (r < 0)
                                 return r;
                 }
@@ -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);
 
@@ -491,16 +488,14 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
                 manager_next_hostname(i->scope->manager);
 }
 
-void dns_zone_item_ready(DnsZoneItem *i) {
-        _cleanup_free_ char *pretty = NULL;
-
+void dns_zone_item_notify(DnsZoneItem *i) {
         assert(i);
         assert(i->probe_transaction);
 
         if (i->block_ready > 0)
                 return;
 
-        if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
+        if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
                 return;
 
         if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
@@ -530,15 +525,13 @@ void dns_zone_item_ready(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 44a8624c3064e123c3e254d537ca1801a5fa851b..dbd6a2a3681d6e381a5e9fbecf77682f2605d802 100644 (file)
@@ -70,7 +70,7 @@ void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
 int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
 
 void dns_zone_item_conflict(DnsZoneItem *i);
-void dns_zone_item_ready(DnsZoneItem *i);
+void dns_zone_item_notify(DnsZoneItem *i);
 
 int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr);
 int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key);
index c815eae850d47bba2d4d621d0756d1b1cf01c659..c5ad04afd77ee336916a62d54118778e81851435 100644 (file)
@@ -14,8 +14,9 @@ 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.MulticastDNS, config_parse_resolve_support, 0,                   offsetof(Manager, mdns_support)
+Resolve.DNSSEC,       config_parse_dnssec_mode,     0,                   offsetof(Manager, dnssec_mode)
index 84100bd988e70bc0ad36320c4259c3a632ec2d4f..928307e004b59b5c2d5d8cce6522162e8654d1fc 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)
@@ -65,7 +67,7 @@ Link *link_free(Link *l) {
         if (!l)
                 return NULL;
 
-        dns_server_unlink_marked(l->dns_servers);
+        dns_server_unlink_all(l->dns_servers);
         dns_search_domain_unlink_all(l->search_domains);
 
         while (l->addresses)
@@ -80,6 +82,8 @@ Link *link_free(Link *l) {
         dns_scope_free(l->mdns_ipv4_scope);
         dns_scope_free(l->mdns_ipv6_scope);
 
+        set_free_free(l->dnssec_negative_trust_anchors);
+
         free(l);
         return NULL;
 }
@@ -99,8 +103,8 @@ static void link_allocate_scopes(Link *l) {
                 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) {
+            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)
@@ -110,8 +114,8 @@ static void link_allocate_scopes(Link *l) {
                 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 &&
+            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);
@@ -122,8 +126,8 @@ static void link_allocate_scopes(Link *l) {
                 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) {
+            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)
@@ -133,8 +137,8 @@ static void link_allocate_scopes(Link *l) {
                 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) {
+            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)
@@ -183,6 +187,10 @@ static int link_update_dns_servers(Link *l) {
         assert(l);
 
         r = sd_network_link_get_dns(l->ifindex, &nameservers);
+        if (r == -ENODATA) {
+                r = 0;
+                goto clear;
+        }
         if (r < 0)
                 goto clear;
 
@@ -222,25 +230,114 @@ static int link_update_llmnr_support(Link *l) {
         assert(l);
 
         r = sd_network_link_get_llmnr(l->ifindex, &b);
+        if (r == -ENODATA) {
+                r = 0;
+                goto clear;
+        }
         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);
 
-        } else if (r > 0)
-                l->llmnr_support = SUPPORT_YES;
-        else
-                l->llmnr_support = SUPPORT_NO;
+        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->llmnr_support = SUPPORT_YES;
+        l->mdns_support = RESOLVE_SUPPORT_NO;
+        return r;
+}
+
+static int link_update_dnssec_mode(Link *l) {
+        _cleanup_free_ char *m = NULL;
+        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;
+
+        l->dnssec_mode = dnssec_mode_from_string(m);
+        if (l->dnssec_mode < 0) {
+                r = -EINVAL;
+                goto clear;
+        }
+
+        return 0;
+
+clear:
+        l->dnssec_mode = _DNSSEC_MODE_INVALID;
+        return r;
+}
+
+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->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
         return r;
 }
 
@@ -252,6 +349,11 @@ static int link_update_search_domains(Link *l) {
         assert(l);
 
         r = sd_network_link_get_domains(l->ifindex, &domains);
+        if (r == -ENODATA) {
+                /* networkd knows nothing about this interface, and that's fine. */
+                r = 0;
+                goto clear;
+        }
         if (r < 0)
                 goto clear;
 
@@ -286,14 +388,31 @@ int link_update_monitor(Link *l) {
 
         assert(l);
 
-        link_update_dns_servers(l);
-        link_update_llmnr_support(l);
-        link_allocate_scopes(l);
+        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);
 
+        link_allocate_scopes(l);
         link_add_rrs(l, false);
 
         return 0;
@@ -344,12 +463,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);
@@ -446,8 +561,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);
@@ -503,8 +618,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..db0e51da04c3ee0e22f1ddea6d3d9f7925abdd37 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;
index 6a7ff9d24589329ce5d85d811502dce74d7907fa..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:
@@ -461,10 +465,10 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) {
         }
 
         r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
-        if (r < 0)  {
-                r = -errno;
+        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;
 
index a2677f442aec2ae1df726f8fb6cbbed3dc7094d8..b17a19d331e4c56f5b739ca9a2baaced2ba5a127 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;
@@ -772,7 +782,7 @@ static int write_loop(int fd, void *message, size_t length) {
 int manager_write(Manager *m, int fd, DnsPacket *p) {
         int r;
 
-        log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
+        log_debug("Sending %s packet with id %" PRIu16 ".", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p));
 
         r = write_loop(fd, DNS_PACKET_DATA(p), p->size);
         if (r < 0)
@@ -887,7 +897,7 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add
         assert(port > 0);
         assert(p);
 
-        log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
+        log_debug("Sending %s packet with id %" PRIu16 " on interface %i/%s.", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
 
         if (family == AF_INET)
                 return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
@@ -1163,10 +1173,3 @@ 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);
index b52273403a5f6d39e1a1786e0deb97c23f0b1845..1907d2e1bc6caecdb54d21357335c7a7f23a172a 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 */
@@ -162,6 +158,3 @@ 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_;
index 096a4b1fe55a7157663df69a2c703079621684c0..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;
@@ -86,7 +86,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
         }
 
         if (dns_packet_validate_reply(p) > 0) {
-                unsigned i;
+                DnsResourceRecord *rr;
 
                 log_debug("Got mDNS reply packet");
 
@@ -107,19 +107,22 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
 
                 dns_scope_check_conflicts(scope, p);
 
-                for (i = 0; i < p->answer->n_rrs; i++) {
-                        DnsResourceRecord *rr;
+                DNS_ANSWER_FOREACH(rr, p->answer) {
+                        const char *name = DNS_RESOURCE_KEY_NAME(rr->key);
                         DnsTransaction *t;
 
-                        rr = p->answer->items[i].rr;
+                        /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
+                         * we assume someone's playing tricks on us and discard the packet completely. */
+                        if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
+                              dns_name_endswith(name, "local") > 0))
+                                return 0;
 
                         t = dns_scope_find_transaction(scope, rr->key, false);
                         if (t)
                                 dns_transaction_process_reply(t, p);
                 }
 
-                dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer,
-                              p->answer->n_rrs, 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));
@@ -275,10 +278,8 @@ int manager_mdns_ipv6_fd(Manager *m) {
         }
 
         r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
-        if (r < 0)  {
-                r = -errno;
+        if (r < 0)
                 goto fail;
-        }
 
         return m->mdns_ipv6_fd;
 
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");
index efc9c6733a3cfb23efe4e53f71f3294ecf21979c..0ba572d1134ac1e718a9a07b3c53ab3ca573ac14 100644 (file)
@@ -16,4 +16,5 @@
 #FallbackDNS=@DNS_SERVERS@
 #Domains=
 #LLMNR=yes
+#MulticastDNS=no
 #DNSSEC=no
index 0b2ffeedddacc84d699b4bafcc000b28f4864d6c..0c9efde1fecb5986a493da616259253176af38f2 100644 (file)
 #include "resolved-dns-dnssec.h"
 #include "resolved-dns-rr.h"
 #include "string-util.h"
+#include "hexdecoct.h"
+
+static void test_dnssec_verify_rrset2(void) {
+
+        static const uint8_t signature_blob[] = {
+                0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11,
+                0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b,
+                0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca,
+                0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2,
+                0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda,
+                0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27,
+                0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50,
+                0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22,
+        };
+
+        static const uint8_t dnskey_blob[] = {
+                0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea,
+                0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3,
+                0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07,
+                0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5,
+                0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7,
+                0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56,
+                0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e,
+                0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49,
+                0x74, 0x62, 0xfe, 0xd7,
+        };
+
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        DnssecResult result;
+
+        nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
+        assert_se(nsec);
+
+        nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov");
+        assert_se(nsec->nsec.next_domain_name);
+
+        nsec->nsec.types = bitmap_new();
+        assert_se(nsec->nsec.types);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0);
+        assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0);
+
+        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);
+
+        rrsig->rrsig.type_covered = DNS_TYPE_NSEC;
+        rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+        rrsig->rrsig.labels = 2;
+        rrsig->rrsig.original_ttl = 300;
+        rrsig->rrsig.expiration = 0x5689002f;
+        rrsig->rrsig.inception = 0x56617230;
+        rrsig->rrsig.key_tag = 30390;
+        rrsig->rrsig.signer = strdup("Nasa.Gov.");
+        assert_se(rrsig->rrsig.signer);
+        rrsig->rrsig.signature_size = sizeof(signature_blob);
+        rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
+        assert_se(rrsig->rrsig.signature);
+
+        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);
+
+        dnskey->dnskey.flags = 256;
+        dnskey->dnskey.protocol = 3;
+        dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+        dnskey->dnskey.key_size = sizeof(dnskey_blob);
+        dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
+        assert_se(dnskey->dnskey.key);
+
+        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, false) > 0);
+
+        answer = dns_answer_new(1);
+        assert_se(answer);
+        assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+
+        /* Validate the RR as it if was 2015-12-11 today */
+        assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0);
+        assert_se(result == DNSSEC_VALIDATED);
+}
 
 static void test_dnssec_verify_rrset(void) {
 
@@ -55,15 +148,14 @@ 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");
         assert_se(a);
 
         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);
@@ -81,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);
@@ -94,19 +185,19 @@ 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);
-        assert_se(dns_answer_add(answer, a, 0) >= 0);
+        assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
 
         /* Validate the RR as it if was 2015-12-2 today */
-        assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, 1449092754*USEC_PER_SEC) == DNSSEC_VERIFIED);
+        assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, 1449092754*USEC_PER_SEC, &result) >= 0);
+        assert_se(result == DNSSEC_VALIDATED);
 }
 
 static void test_dnssec_verify_dns_key(void) {
@@ -140,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");
@@ -153,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);
@@ -166,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);
@@ -179,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(dnskey, ds1, false) > 0);
+        assert_se(dnssec_verify_dnskey(dnskey, ds2, false) > 0);
 }
 
 static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) {
@@ -207,11 +294,45 @@ static void test_dnssec_canonicalize(void) {
         test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL);
 }
 
+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;
+        uint8_t h[DNSSEC_HASH_SIZE_MAX];
+        _cleanup_free_ char *b = NULL;
+        int k;
+
+        /* The NSEC3 RR for eurid.eu on 2015-12-14. */
+        rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC3, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM.eurid.eu.");
+        assert_se(rr);
+
+        rr->nsec3.algorithm = DNSSEC_DIGEST_SHA1;
+        rr->nsec3.flags = 1;
+        rr->nsec3.iterations = 1;
+        rr->nsec3.salt = memdup(salt, sizeof(salt));
+        assert_se(rr->nsec3.salt);
+        rr->nsec3.salt_size = sizeof(salt);
+        rr->nsec3.next_hashed_name = memdup(next_hashed_name, sizeof(next_hashed_name));
+        assert_se(rr->nsec3.next_hashed_name);
+        rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name);
+
+        log_info("NSEC3: %s", strna(dns_resource_record_to_string(rr)));
+
+        k = dnssec_nsec3_hash(rr, "eurid.eu", &h);
+        assert_se(k >= 0);
+
+        b = base32hexmem(h, k, false);
+        assert_se(b);
+        assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0);
+}
+
 int main(int argc, char*argv[]) {
 
         test_dnssec_canonicalize();
         test_dnssec_verify_dns_key();
         test_dnssec_verify_rrset();
+        test_dnssec_verify_rrset2();
+        test_dnssec_nsec3_hash();
 
         return 0;
 }
index e427cc147013ff1174199b84cc4d9cbd47d0ff6c..5c6dc34700c4a203323063f08220c19e87eb9c86 100644 (file)
@@ -2381,23 +2381,28 @@ int bus_property_get_rlimit(
         struct rlimit *rl;
         uint64_t u;
         rlim_t x;
+        const char *is_soft;
 
         assert(bus);
         assert(reply);
         assert(userdata);
 
+        is_soft = endswith(property, "Soft");
         rl = *(struct rlimit**) userdata;
         if (rl)
-                x = rl->rlim_max;
+                x = is_soft ? rl->rlim_cur : rl->rlim_max;
         else {
                 struct rlimit buf = {};
                 int z;
+                const char *s;
+
+                s = is_soft ? strndupa(property, is_soft - property) : property;
 
-                z = rlimit_from_string(strstr(property, "Limit"));
+                z = rlimit_from_string(strstr(s, "Limit"));
                 assert(z >= 0);
 
                 getrlimit(z, &buf);
-                x = buf.rlim_max;
+                x = is_soft ? buf.rlim_cur : buf.rlim_max;
         }
 
         /* rlim_t might have different sizes, let's map
index 0466857042acf87fc761a9427c637d0e51c4a40b..59475115ba7bca88f67f8902923dbf9801915668 100644 (file)
@@ -48,7 +48,6 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
 
         assert(name);
         assert(*name);
-        assert(dest);
 
         n = *name;
         d = dest;
@@ -79,9 +78,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
 
                         else if (*n == '\\' || *n == '.') {
                                 /* Escaped backslash or dot */
-                                *(d++) = *(n++);
+
+                                if (d)
+                                        *(d++) = *n;
                                 sz--;
                                 r++;
+                                n++;
 
                         } else if (n[0] >= '0' && n[0] <= '9') {
                                 unsigned k;
@@ -96,11 +98,17 @@ 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;
 
-                                *(d++) = (char) k;
+                                if (d)
+                                        *(d++) = (char) k;
                                 sz--;
                                 r++;
 
@@ -111,9 +119,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
                 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
 
                         /* Normal character */
-                        *(d++) = *(n++);
+
+                        if (d)
+                                *(d++) = *n;
                         sz--;
                         r++;
+                        n++;
                 } else
                         return -EINVAL;
         }
@@ -122,7 +133,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
         if (r == 0 && *n)
                 return -EINVAL;
 
-        if (sz >= 1)
+        if (sz >= 1 && d)
                 *d = 0;
 
         *name = n;
@@ -148,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;
@@ -170,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 {
@@ -235,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 */
 
@@ -249,8 +264,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
 
                         sz -= 4;
 
-                } else
-                        return -EINVAL;
+                }
 
                 p++;
                 l--;
@@ -472,13 +486,15 @@ 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;
+                if (r == 0)
+                        break;
 
                 k = dns_label_undo_idna(label, r, label, sizeof(label));
                 if (k < 0)
@@ -486,13 +502,9 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
                 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 */
@@ -527,7 +539,7 @@ int dns_name_compare_func(const void *a, const void *b) {
                 if (k > 0)
                         r = k;
                 if (w > 0)
-                        r = w;
+                        q = w;
 
                 la[r] = lb[q] = 0;
                 r = strcasecmp(la, lb);
@@ -899,19 +911,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;
@@ -1153,3 +1157,77 @@ finish:
 
         return 0;
 }
+
+int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
+        const char* labels[DNS_N_LABELS_MAX+1];
+        unsigned n = 0;
+        const char *p;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        p = name;
+        for (;;) {
+                if (n > DNS_N_LABELS_MAX)
+                        return -EINVAL;
+
+                labels[n] = p;
+
+                r = dns_name_parent(&p);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                n++;
+        }
+
+        if (n < n_labels)
+                return -EINVAL;
+
+        *ret = labels[n - n_labels];
+        return (int) (n - n_labels);
+}
+
+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);
+
+        while (n_labels > 0) {
+
+                r = dns_name_parent(&a);
+                if (r <= 0)
+                        return r;
+
+                n_labels --;
+        }
+
+        return dns_name_equal(a, b);
+}
index 3f8f621802f9df7d4e648a127831952816fc3f73..dd8ae3ac9880d3f861e13051da4efbd88c024388 100644 (file)
 /* 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);
 int dns_label_escape_new(const char *p, size_t l, char **ret);
 
+static inline int dns_name_parent(const char **name) {
+        return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
+}
+
 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
 
@@ -92,3 +99,8 @@ 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_equal_skip(const char *a, unsigned n_labels, const char *b);
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
index 29ce732b56fb2de693314e1b605432b4e4c0a48c..ad400e869319d803ad625b83681895c4e16827c2 100644 (file)
@@ -160,58 +160,6 @@ int raw_strip_suffixes(const char *p, char **ret) {
         return 0;
 }
 
-bool dkr_digest_is_valid(const char *digest) {
-        /* 7 chars for prefix, 64 chars for the digest itself */
-        if (strlen(digest) != 71)
-                return false;
-
-        return startswith(digest, "sha256:") && in_charset(digest + 7, "0123456789abcdef");
-}
-
-bool dkr_ref_is_valid(const char *ref) {
-        const char *colon;
-
-        if (isempty(ref))
-                return false;
-
-        colon = strchr(ref, ':');
-        if (!colon)
-                return filename_is_valid(ref);
-
-        return dkr_digest_is_valid(ref);
-}
-
-bool dkr_name_is_valid(const char *name) {
-        const char *slash, *p;
-
-        if (isempty(name))
-                return false;
-
-        slash = strchr(name, '/');
-        if (!slash)
-                return false;
-
-        if (!filename_is_valid(slash + 1))
-                return false;
-
-        p = strndupa(name, slash - name);
-        if (!filename_is_valid(p))
-                return false;
-
-        return true;
-}
-
-bool dkr_id_is_valid(const char *id) {
-
-        if (!filename_is_valid(id))
-                return false;
-
-        if (!in_charset(id, "0123456789abcdef"))
-                return false;
-
-        return true;
-}
-
 int import_assign_pool_quota_and_warn(const char *path) {
         int r;
 
index 9120a5119f47739b42586670b0217a054455c0f4..4bfa2d9aaeec4e4f12974ceca2c14394107f7474 100644 (file)
@@ -42,10 +42,4 @@ ImportVerify import_verify_from_string(const char *s) _pure_;
 int tar_strip_suffixes(const char *name, char **ret);
 int raw_strip_suffixes(const char *name, char **ret);
 
-bool dkr_name_is_valid(const char *name);
-bool dkr_id_is_valid(const char *id);
-bool dkr_ref_is_valid(const char *ref);
-bool dkr_digest_is_valid(const char *digest);
-#define dkr_tag_is_valid(tag) filename_is_valid(tag)
-
 int import_assign_pool_quota_and_warn(const char *path);
index 193dad194372a3cc35348be45be05a8b19273cc3..a1f65d1a888f38f7abaefa1e759c04e452bf07dc 100644 (file)
@@ -435,8 +435,9 @@ static int output_verbose(
 
                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
                 if (r < 0)
-                        log_debug_errno(r, "_SOURCE_REALTIME_TIMESTAMP invalid: %m");
+                        return r;
                 else {
+                        assert(r > 0);
                         r = safe_atou64(value, &realtime);
                         if (r < 0)
                                 log_debug_errno(r, "Failed to parse realtime timestamp: %m");
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 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 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..6c3c49908f14716724cb716919256325f909fbd7 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);
@@ -475,6 +475,90 @@ 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);
+}
+
 int main(int argc, char *argv[]) {
 
         test_dns_label_unescape();
@@ -495,6 +579,10 @@ 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();
 
         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 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 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 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 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 bb3aaaf821950e6a0eb99e7c3922a56bb2e41f67..c065034d2986b560f72bde58dde159f7fae082a3 100644 (file)
@@ -1,2 +1,3 @@
 /basic.conf
 /systemd.conf
+/systemd-remote.conf
similarity index 86%
rename from sysusers.d/systemd-remote.conf
rename to sysusers.d/systemd-remote.conf.m4
index 15969e9a97e1eeeda891c51d8e18664d8be1e94c..0e9d71cdd0a9c1db37997523ca8b20c23016ab41 100644 (file)
@@ -5,6 +5,10 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
+m4_ifdef(`HAVE_MICROHTTPD',
 u systemd-journal-gateway - "systemd Journal Gateway"
 u systemd-journal-remote  - "systemd Journal Remote"
+)m4_dnl
+m4_ifdef(`HAVE_LIBCURL',
 u systemd-journal-upload  - "systemd Journal Upload"
+)m4_dnl
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-05-RLIMITS/Makefile b/test/TEST-05-RLIMITS/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-05-RLIMITS/test-rlimits.sh b/test/TEST-05-RLIMITS/test-rlimits.sh
new file mode 100755 (executable)
index 0000000..54000ec
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -x
+set -e
+set -o pipefail
+
+[[ "$(systemctl show -p DefaultLimitNOFILESoft)" = "DefaultLimitNOFILESoft=10000" ]]
+[[ "$(systemctl show -p DefaultLimitNOFILE)" = "DefaultLimitNOFILE=16384" ]]
+
+[[ "$(systemctl show -p LimitNOFILESoft testsuite.service)" = "LimitNOFILESoft=10000" ]]
+[[ "$(systemctl show -p LimitNOFILE testsuite.service)" = "LimitNOFILE=16384" ]]
+
+[[ "$(ulimit -n -S)" = "10000" ]]
+[[ "$(ulimit -n -H)" = "16384" ]]
+
+touch /testok
+exit 0
diff --git a/test/TEST-05-RLIMITS/test.sh b/test/TEST-05-RLIMITS/test.sh
new file mode 100755 (executable)
index 0000000..6eaa0b8
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+TEST_DESCRIPTION="Resource limits-related tests"
+
+. $TEST_BASE_DIR/test-functions
+
+check_result_qemu() {
+    ret=1
+    mkdir -p $TESTDIR/root
+    mount ${LOOPDEV}p1 $TESTDIR/root
+    [[ -e $TESTDIR/root/testok ]] && ret=0
+    [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR
+    cp -a $TESTDIR/root/var/log/journal $TESTDIR
+    umount $TESTDIR/root
+    [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
+    ls -l $TESTDIR/journal/*/*.journal
+    test -s $TESTDIR/failed && ret=$(($ret+1))
+    return $ret
+}
+
+test_run() {
+    if run_qemu; then
+        check_result_qemu || return 1
+    else
+        dwarn "can't run QEMU, skipping"
+    fi
+    if check_nspawn; then
+        run_nspawn
+        check_result_nspawn || return 1
+    else
+        dwarn "can't run systemd-nspawn, skipping"
+    fi
+    return 0
+}
+
+test_setup() {
+    create_empty_image
+    mkdir -p $TESTDIR/root
+    mount ${LOOPDEV}p1 $TESTDIR/root
+
+    # Create what will eventually be our root filesystem onto an overlay
+    (
+        LOG_LEVEL=5
+        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+        setup_basic_environment
+
+        cat >$initdir/etc/systemd/system.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+        # setup the testsuite service
+        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+After=multi-user.target
+
+[Service]
+ExecStart=/test-rlimits.sh
+Type=oneshot
+EOF
+
+        cp test-rlimits.sh $initdir/
+
+        setup_testsuite
+    ) || return 1
+    setup_nspawn_root
+
+    ddebug "umount $TESTDIR/root"
+    umount $TESTDIR/root
+}
+
+test_cleanup() {
+    umount $TESTDIR/root 2>/dev/null
+    [[ $LOOPDEV ]] && losetup -d $LOOPDEV
+    return 0
+}
+
+do_test "$@"
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 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 987220e554208dbf6a0a2eaa4b06d6453a7f8899..f4f845841da0f80a7c54447a764084b3c7d66d98 100644 (file)
@@ -7,6 +7,7 @@
 
 [Unit]
 Description=Journal Gateway Service
+Documentation=man:systemd-journal-gatewayd(8)
 Requires=systemd-journal-gatewayd.socket
 
 [Service]
index fd11058ab4ae5210b481c2a5a0a019b01a255a88..79d9b04210130cdb8e55759b00f2749d63d27d5d 100644 (file)
@@ -7,6 +7,7 @@
 
 [Unit]
 Description=Journal Gateway Service Socket
+Documentation=man:systemd-journal-gatewayd(8)
 
 [Socket]
 ListenStream=19531
index 2928a230211737402b4ffa830458b0a5fcf67a9d..fdf3da4b642ebfe9c4e4a43261e7411621fbfe00 100644 (file)
@@ -7,6 +7,7 @@
 
 [Unit]
 Description=Journal Remote Sink Service
+Documentation=man:systemd-journal-remote(8) man:journal-remote.conf(5)
 Requires=systemd-journal-remote.socket
 
 [Service]
index a757673a621b13504a5fa53089b21b9008f21830..4a89186f31dc5085929acce35f6b1ed744939e44 100644 (file)
@@ -7,6 +7,7 @@
 
 [Unit]
 Description=Journal Remote Upload Service
+Documentation=man:systemd-journal-upload(8)
 After=network.target
 
 [Service]