diff options
202 files changed, 5306 insertions, 729 deletions
diff --git a/.gitignore b/.gitignore index c400fea684..4d9cbbe4a0 100644 --- a/.gitignore +++ b/.gitignore @@ -182,6 +182,7 @@ /test-dhcp-server /test-dhcp6-client /test-dns-domain +/test-dnssec /test-efi-disk.img /test-ellipsize /test-engine @@ -245,9 +246,11 @@ /test-pty /test-qcow2 /test-ratelimit +/test-rbtree /test-replace-var /test-resolve /test-ring +/test-rlimit-util /test-sched-prio /test-set /test-sigbus diff --git a/Makefile.am b/Makefile.am index cecd139e4f..e99c937e61 100644 --- a/Makefile.am +++ b/Makefile.am @@ -147,6 +147,7 @@ tests= manual_tests = TEST_EXTENSIONS = .py PY_LOG_COMPILER = $(PYTHON) +DISABLE_HARD_ERRORS = yes if ENABLE_TESTS noinst_PROGRAMS = $(manual_tests) $(tests) TESTS = $(tests) @@ -744,8 +745,6 @@ CLEANFILES += \ man/systemd.index.xml \ man/systemd.directives.xml -EXTRA_DIST += \ - tools/make-man-rules.py endif @@ -754,6 +753,7 @@ endif EXTRA_DIST += \ $(filter-out man/systemd.directives.xml man/systemd.index.xml,$(XML_FILES)) \ tools/make-man-index.py \ + tools/make-man-rules.py \ tools/make-directive-index.py \ tools/xml_helper.py \ man/glib-event-glue.c @@ -766,6 +766,8 @@ libbasic_la_SOURCES = \ src/basic/missing.h \ src/basic/capability-util.c \ src/basic/capability-util.h \ + src/basic/c-rbtree.c \ + src/basic/c-rbtree.h \ src/basic/conf-files.c \ src/basic/conf-files.h \ src/basic/stdio-util.h \ @@ -1493,11 +1495,13 @@ tests += \ test-copy \ test-cap-list \ test-sigbus \ + test-rbtree \ test-verbs \ test-af-list \ test-arphrd-list \ test-dns-domain \ - test-install-root + test-install-root \ + test-rlimit-util if HAVE_ACL tests += \ @@ -1728,6 +1732,12 @@ test_sigbus_SOURCES = \ test_sigbus_LDADD = \ libshared.la +test_rbtree_SOURCES = \ + src/test/test-rbtree.c + +test_rbtree_LDADD = \ + libshared.la + test_condition_SOURCES = \ src/test/test-condition.c @@ -1860,6 +1870,12 @@ test_acl_util_LDADD = \ test_namespace_LDADD = \ libcore.la +test_rlimit_util_SOURCES = \ + src/test/test-rlimit-util.c + +test_rlimit_util_LDADD = \ + libshared.la + BUILT_SOURCES += \ src/test/test-hashmap-ordered.c @@ -4816,9 +4832,6 @@ systemd_timesyncd_SOURCES = \ nodist_systemd_timesyncd_SOURCES = \ src/timesync/timesyncd-gperf.c -gperf_gperf_sources += \ - src/timesync/timesyncd-gperf.gperf - systemd_timesyncd_LDADD = \ libsystemd-network.la \ libshared.la @@ -4832,15 +4845,18 @@ nodist_systemunit_DATA += \ GENERAL_ALIASES += \ $(systemunitdir)/systemd-timesyncd.service $(pkgsysconfdir)/system/sysinit.target.wants/systemd-timesyncd.service -EXTRA_DIST += \ - units/systemd-timesyncd.service.in nodist_pkgsysconf_DATA += \ src/timesync/timesyncd.conf +endif + +gperf_gperf_sources += \ + src/timesync/timesyncd-gperf.gperf + EXTRA_DIST += \ + units/systemd-timesyncd.service.in \ src/timesync/timesyncd.conf.in -endif # ------------------------------------------------------------------------------ if HAVE_MYHOSTNAME @@ -5136,12 +5152,13 @@ polkitpolicy_in_files += \ src/import/org.freedesktop.import1.policy.in EXTRA_DIST += \ - units/systemd-importd.service.in \ - src/resolve/resolved.conf.in + units/systemd-importd.service.in # ------------------------------------------------------------------------------ if ENABLE_RESOLVED +if HAVE_GCRYPT + systemd_resolved_SOURCES = \ src/resolve/resolved.c \ src/resolve/resolved-manager.c \ @@ -5156,6 +5173,8 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-link.c \ src/resolve/resolved-llmnr.h \ src/resolve/resolved-llmnr.c \ + src/resolve/resolved-mdns.h \ + src/resolve/resolved-mdns.c \ src/resolve/resolved-def.h \ src/resolve/resolved-dns-rr.h \ src/resolve/resolved-dns-rr.c \ @@ -5181,6 +5200,10 @@ systemd_resolved_SOURCES = \ src/resolve/resolved-dns-zone.c \ src/resolve/resolved-dns-stream.h \ src/resolve/resolved-dns-stream.c \ + src/resolve/resolved-dns-dnssec.h \ + src/resolve/resolved-dns-dnssec.c \ + src/resolve/resolved-dns-trust-anchor.h \ + src/resolve/resolved-dns-trust-anchor.c \ src/resolve/dns-type.c \ src/resolve/dns-type.h @@ -5189,12 +5212,6 @@ nodist_systemd_resolved_SOURCES = \ src/resolve/dns_type-to-name.h \ src/resolve/resolved-gperf.c -gperf_gperf_sources += \ - src/resolve/resolved-gperf.gperf - -gperf_txt_sources += \ - src/resolve/dns_type-list.txt - systemd_resolved_LDADD = \ libsystemd-network.la \ libshared.la @@ -5226,9 +5243,6 @@ GENERAL_ALIASES += \ nodist_pkgsysconf_DATA += \ src/resolve/resolved.conf -tests += \ - test-dns-domain - libnss_resolve_la_SOURCES = \ src/nss-resolve/nss-resolve.sym \ src/nss-resolve/nss-resolve.c @@ -5272,10 +5286,40 @@ systemd_resolve_host_LDADD = \ rootlibexec_PROGRAMS += \ systemd-resolve-host +tests += \ + test-dns-domain \ + test-dnssec + +test_dnssec_SOURCES = \ + src/resolve/test-dnssec.c \ + src/resolve/resolved-dns-packet.c \ + src/resolve/resolved-dns-packet.h \ + src/resolve/resolved-dns-rr.c \ + src/resolve/resolved-dns-rr.h \ + src/resolve/resolved-dns-answer.c \ + src/resolve/resolved-dns-answer.h \ + src/resolve/resolved-dns-question.c \ + src/resolve/resolved-dns-question.h \ + src/resolve/resolved-dns-dnssec.c \ + src/resolve/resolved-dns-dnssec.h \ + src/resolve/dns-type.c \ + src/resolve/dns-type.h + +test_dnssec_LDADD = \ + libshared.la + +endif endif +gperf_txt_sources += \ + src/resolve/dns_type-list.txt + +gperf_gperf_sources += \ + src/resolve/resolved-gperf.gperf + EXTRA_DIST += \ - units/systemd-resolved.service.m4.in + units/systemd-resolved.service.m4.in \ + src/resolve/resolved.conf.in # ------------------------------------------------------------------------------ if ENABLE_NETWORKD @@ -5443,14 +5487,16 @@ SYSTEM_UNIT_ALIASES += \ BUSNAMES_TARGET_WANTS += \ org.freedesktop.network1.busname +endif + gperf_gperf_sources += \ src/network/networkd-network-gperf.gperf \ src/network/networkd-netdev-gperf.gperf -endif EXTRA_DIST += \ units/systemd-networkd.service.m4.in \ - units/systemd-networkd-wait-online.service.in + units/systemd-networkd-wait-online.service.in \ + test/networkd-test.py # ------------------------------------------------------------------------------ if ENABLE_LOGIND @@ -5691,6 +5737,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 += \ @@ -6181,21 +6230,7 @@ DISTCHECK_CONFIGURE_FLAGS += \ --disable-split-usr endif -# -# Require python when making dist -# -.PHONY: dist-check-python dist-check-compat-libs dist-check-help -dist-check-python: -if !HAVE_PYTHON - @echo "*** python and python-lxml module must be installed and enabled in order to make dist" - @false -endif - -dist-check-compat-libs: -if !ENABLE_COMPAT_LIBS - @echo "*** compat-libs must be enabled in order to make dist" - @false -endif +.PHONY: dist-check-help dist-check-help: $(rootbin_PROGRAMS) $(bin_PROGRAMS) for i in $(abspath $^); do \ @@ -6205,8 +6240,6 @@ dist-check-help: $(rootbin_PROGRAMS) $(bin_PROGRAMS) exit 1; \ fi; done -dist: dist-check-python dist-check-compat-libs - .PHONY: hwdb-update hwdb-update: ( cd $(top_srcdir)/hwdb && \ diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 1aff9d5329..acd48f6954 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -49,9 +49,9 @@ #include "selinux-util.h" #include "smack-util.h" #include "sparse-endian.h" -#include "time-util.h" #include "stat-util.h" #include "string-util.h" +#include "time-util.h" #include "util.h" /* WARNING: Be careful with file system ioctls! When we get an fd, we diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c new file mode 100644 index 0000000000..914d7e5229 --- /dev/null +++ b/src/basic/c-rbtree.c @@ -0,0 +1,679 @@ +/*** + This file is part of systemd. See COPYING for details. + + 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/>. +***/ + +/* + * RB-Tree Implementation + * This implements the insertion/removal of elements in RB-Trees. You're highly + * recommended to have an RB-Tree documentation at hand when reading this. Both + * insertion and removal can be split into a handful of situations that can + * occur. Those situations are enumerated as "Case 1" to "Case n" here, and + * follow closely the cases described in most RB-Tree documentations. This file + * does not explain why it is enough to handle just those cases, nor does it + * provide a proof of correctness. Dig out your algorithm 101 handbook if + * you're interested. + * + * This implementation is *not* straightforward. Usually, a handful of + * rotation, reparent, swap and link helpers can be used to implement the + * rebalance operations. However, those often perform unnecessary writes. + * Therefore, this implementation hard-codes all the operations. You're highly + * recommended to look at the two basic helpers before reading the code: + * c_rbtree_swap_child() + * c_rbtree_set_parent_and_color() + * Those are the only helpers used, hence, you should really know what they do + * before digging into the code. + * + * For a highlevel documentation of the API, see the header file and docbook + * comments. + */ + +#include <assert.h> +#include <stddef.h> +#include "c-rbtree.h" + +enum { + C_RBNODE_RED = 0, + C_RBNODE_BLACK = 1, +}; + +static inline unsigned long c_rbnode_color(CRBNode *n) { + return (unsigned long)n->__parent_and_color & 1UL; +} + +static inline _Bool c_rbnode_is_red(CRBNode *n) { + return c_rbnode_color(n) == C_RBNODE_RED; +} + +static inline _Bool c_rbnode_is_black(CRBNode *n) { + return c_rbnode_color(n) == C_RBNODE_BLACK; +} + +/** + * c_rbnode_leftmost() - return leftmost child + * @n: current node, or NULL + * + * This returns the leftmost child of @n. If @n is NULL, this will return NULL. + * In all other cases, this function returns a valid pointer. That is, if @n + * does not have any left children, this returns @n. + * + * Worst case runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to leftmost child, or NULL. + */ +CRBNode *c_rbnode_leftmost(CRBNode *n) { + if (n) + while (n->left) + n = n->left; + return n; +} + +/** + * c_rbnode_rightmost() - return rightmost child + * @n: current node, or NULL + * + * This returns the rightmost child of @n. If @n is NULL, this will return + * NULL. In all other cases, this function returns a valid pointer. That is, if + * @n does not have any right children, this returns @n. + * + * Worst case runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to rightmost child, or NULL. + */ +CRBNode *c_rbnode_rightmost(CRBNode *n) { + if (n) + while (n->right) + n = n->right; + return n; +} + +/** + * c_rbnode_next() - return next node + * @n: current node, or NULL + * + * An RB-Tree always defines a linear order of its elements. This function + * returns the logically next node to @n. If @n is NULL, the last node or + * unlinked, this returns NULL. + * + * Worst case runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to next node, or NULL. + */ +CRBNode *c_rbnode_next(CRBNode *n) { + CRBNode *p; + + if (!c_rbnode_is_linked(n)) + return NULL; + if (n->right) + return c_rbnode_leftmost(n->right); + + while ((p = c_rbnode_parent(n)) && n == p->right) + n = p; + + return p; +} + +/** + * c_rbnode_prev() - return previous node + * @n: current node, or NULL + * + * An RB-Tree always defines a linear order of its elements. This function + * returns the logically previous node to @n. If @n is NULL, the first node or + * unlinked, this returns NULL. + * + * Worst case runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to previous node, or NULL. + */ +CRBNode *c_rbnode_prev(CRBNode *n) { + CRBNode *p; + + if (!c_rbnode_is_linked(n)) + return NULL; + if (n->left) + return c_rbnode_rightmost(n->left); + + while ((p = c_rbnode_parent(n)) && n == p->left) + n = p; + + return p; +} + +/** + * c_rbtree_first() - return first node + * @t: tree to operate on + * + * An RB-Tree always defines a linear order of its elements. This function + * returns the logically first node in @t. If @t is empty, NULL is returned. + * + * Fixed runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to first node, or NULL. + */ +CRBNode *c_rbtree_first(CRBTree *t) { + assert(t); + return c_rbnode_leftmost(t->root); +} + +/** + * c_rbtree_last() - return last node + * @t: tree to operate on + * + * An RB-Tree always defines a linear order of its elements. This function + * returns the logically last node in @t. If @t is empty, NULL is returned. + * + * Fixed runtime (n: number of elements in tree): O(log(n)) + * + * Return: Pointer to last node, or NULL. + */ +CRBNode *c_rbtree_last(CRBTree *t) { + assert(t); + return c_rbnode_rightmost(t->root); +} + +/* + * Set the color and parent of a node. This should be treated as a simple + * assignment of the 'color' and 'parent' fields of the node. No other magic is + * applied. But since both fields share its backing memory, this helper + * function is provided. + */ +static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) { + assert(!((unsigned long)p & 1)); + assert(c < 2); + n->__parent_and_color = (CRBNode*)((unsigned long)p | c); +} + +/* same as c_rbnode_set_parent_and_color(), but keeps the current parent */ +static inline void c_rbnode_set_color(CRBNode *n, unsigned long c) { + c_rbnode_set_parent_and_color(n, c_rbnode_parent(n), c); +} + +/* same as c_rbnode_set_parent_and_color(), but keeps the current color */ +static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) { + c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n)); +} + +/* + * This function partially replaces an existing child pointer to a new one. The + * existing child must be given as @old, the new child as @new. @p must be the + * parent of @old (or NULL if it has no parent). + * This function ensures that the parent of @old now points to @new. However, + * it does *NOT* change the parent pointer of @new. The caller must ensure + * this. + * If @p is NULL, this function ensures that the root-pointer is adjusted + * instead (given as @t). + */ +static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) { + if (p) { + if (p->left == old) + p->left = new; + else + p->right = new; + } else { + t->root = new; + } +} + +static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) { + CRBNode *p, *g, *gg, *u, *x; + + /* + * Paint a single node according to RB-Tree rules. The node must + * already be linked into the tree and painted red. + * We repaint the node or rotate the tree, if required. In case a + * recursive repaint is required, the next node to be re-painted + * is returned. + * p: parent + * g: grandparent + * gg: grandgrandparent + * u: uncle + * x: temporary + */ + + /* node is red, so we can access the parent directly */ + p = n->__parent_and_color; + + if (!p) { + /* Case 1: + * We reached the root. Mark it black and be done. As all + * leaf-paths share the root, the ratio of black nodes on each + * path stays the same. */ + c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK); + n = NULL; + } else if (c_rbnode_is_black(p)) { + /* Case 2: + * The parent is already black. As our node is red, we did not + * change the number of black nodes on any path, nor do we have + * multiple consecutive red nodes. */ + n = NULL; + } else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */ + g = p->__parent_and_color; + gg = c_rbnode_parent(g); + u = g->right; + + if (u && c_rbnode_is_red(u)) { + /* Case 3: + * Parent and uncle are both red. We know the + * grandparent must be black then. Repaint parent and + * uncle black, the grandparent red and recurse into + * the grandparent. */ + c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); + n = g; + } else { + /* parent is red, uncle is black */ + + if (n == p->right) { + /* Case 4: + * We're the right child. Rotate on parent to + * become left child, so we can handle it the + * same as case 5. */ + x = n->left; + p->right = n->left; + n->left = p; + if (x) + c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); + p = n; + } + + /* 'n' is invalid from here on! */ + n = NULL; + + /* Case 5: + * We're the red left child or a red parent, black + * grandparent and uncle. Rotate on grandparent and + * switch color with parent. Number of black nodes on + * each path stays the same, but we got rid of the + * double red path. As the grandparent is still black, + * we're done. */ + x = p->right; + g->left = x; + p->right = g; + if (x) + c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); + c_rbtree_swap_child(t, gg, g, p); + } + } else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */ + g = p->__parent_and_color; + gg = c_rbnode_parent(g); + u = g->left; + + if (u && c_rbnode_is_red(u)) { + c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); + n = g; + } else { + if (n == p->left) { + x = n->right; + p->left = n->right; + n->right = p; + if (x) + c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); + p = n; + } + + n = NULL; + + x = p->left; + g->right = x; + p->left = g; + if (x) + c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); + c_rbtree_swap_child(t, gg, g, p); + } + } + + return n; +} + +static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) { + assert(t); + assert(n); + + while (n) + n = c_rbtree_paint_one(t, n); +} + +/** + * c_rbtree_add() - add node to tree + * @t: tree to operate one + * @p: parent node to link under, or NULL + * @l: left/right slot of @p (or root) to link at + * @n: node to add + * + * This links @n into the tree given as @t. The caller must provide the exact + * spot where to link the node. That is, the caller must traverse the tree + * based on their search order. Once they hit a leaf where to insert the node, + * call this function to link it and rebalance the tree. + * + * A typical insertion would look like this (@t is your tree, @n is your node): + * + * CRBNode **i, *p; + * + * i = &t->root; + * p = NULL; + * while (*i) { + * p = *i; + * if (compare(n, *i) < 0) + * i = &(*i)->left; + * else + * i = &(*i)->right; + * } + * + * c_rbtree_add(t, p, i, n); + * + * Once the node is linked into the tree, a simple lookup on the same tree can + * be coded like this: + * + * CRBNode *i; + * + * i = t->root; + * while (i) { + * int v = compare(n, i); + * if (v < 0) + * i = (*i)->left; + * else if (v > 0) + * i = (*i)->right; + * else + * break; + * } + * + * When you add nodes to a tree, the memory contents of the node do not matter. + * That is, there is no need to initialize the node via c_rbnode_init(). + * However, if you relink nodes multiple times during their lifetime, it is + * usually very convenient to use c_rbnode_init() and c_rbtree_remove_init(). + * In those cases, you should validate that a node is unlinked before you call + * c_rbtree_add(). + */ +void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) { + assert(t); + assert(l); + assert(n); + assert(!p || l == &p->left || l == &p->right); + assert(p || l == &t->root); + + c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED); + n->left = n->right = NULL; + *l = n; + + c_rbtree_paint(t, n); +} + +static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) { + CRBNode *s, *x, *y, *g; + + /* + * Rebalance tree after a node was removed. This happens only if you + * remove a black node and one path is now left with an unbalanced + * number or black nodes. + * This function assumes all paths through p and n have one black node + * less than all other paths. If recursive fixup is required, the + * current node is returned. + */ + + if (n == p->left) { + s = p->right; + if (c_rbnode_is_red(s)) { + /* Case 3: + * We have a red node as sibling. Rotate it onto our + * side so we can later on turn it black. This way, we + * gain the additional black node in our path. */ + g = c_rbnode_parent(p); + x = s->left; + p->right = x; + s->left = p; + c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); + c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); + c_rbtree_swap_child(t, g, p, s); + s = x; + } + + x = s->right; + if (!x || c_rbnode_is_black(x)) { + y = s->left; + if (!y || c_rbnode_is_black(y)) { + /* Case 4: + * Our sibling is black and has only black + * children. Flip it red and turn parent black. + * This way we gained a black node in our path, + * or we fix it recursively one layer up, which + * will rotate the red sibling as parent. */ + c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); + if (c_rbnode_is_black(p)) + return p; + + c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); + return NULL; + } + + /* Case 5: + * Left child of our sibling is red, right one is black. + * Rotate on parent so the right child of our sibling is + * now red, and we can fall through to case 6. */ + x = y->right; + s->left = y->right; + y->right = s; + p->right = y; + if (x) + c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); + x = s; + s = y; + } + + /* Case 6: + * The right child of our sibling is red. Rotate left and flip + * colors, which gains us an additional black node in our path, + * that was previously on our sibling. */ + g = c_rbnode_parent(p); + y = s->left; + p->right = y; + s->left = p; + c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); + if (y) + c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); + c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); + c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); + c_rbtree_swap_child(t, g, p, s); + } else /* if (!n || n == p->right) */ { /* same as above, but mirrored */ + s = p->left; + if (c_rbnode_is_red(s)) { + g = c_rbnode_parent(p); + x = s->right; + p->left = x; + s->right = p; + c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK); + c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); + c_rbtree_swap_child(t, g, p, s); + s = x; + } + + x = s->left; + if (!x || c_rbnode_is_black(x)) { + y = s->right; + if (!y || c_rbnode_is_black(y)) { + c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); + if (c_rbnode_is_black(p)) + return p; + + c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); + return NULL; + } + + x = y->left; + s->right = y->left; + y->left = s; + p->left = y; + if (x) + c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); + x = s; + s = y; + } + + g = c_rbnode_parent(p); + y = s->right; + p->left = y; + s->right = p; + c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); + if (y) + c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); + c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); + c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); + c_rbtree_swap_child(t, g, p, s); + } + + return NULL; +} + +static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) { + CRBNode *n = NULL; + + assert(t); + assert(p); + + do { + n = c_rbtree_rebalance_one(t, p, n); + p = n ? c_rbnode_parent(n) : NULL; + } while (p); +} + +/** + * c_rbtree_remove() - remove node from tree + * @t: tree to operate one + * @n: node to remove + * + * This removes the given node from its tree. Once unlinked, the tree is + * rebalanced. + * The caller *must* ensure that the given tree is actually the tree it is + * linked on. Otherwise, behavior is undefined. + * + * This does *NOT* reset @n to being unlinked (for performance reason, this + * function *never* modifies @n at all). If you need this, use + * c_rbtree_remove_init(). + */ +void c_rbtree_remove(CRBTree *t, CRBNode *n) { + CRBNode *p, *s, *gc, *x, *next = NULL; + unsigned long c; + + assert(t); + assert(n); + assert(c_rbnode_is_linked(n)); + + /* + * There are three distinct cases during node removal of a tree: + * * The node has no children, in which case it can simply be removed. + * * The node has exactly one child, in which case the child displaces + * its parent. + * * The node has two children, in which case there is guaranteed to + * be a successor to the node (successor being the node ordered + * directly after it). This successor cannot have two children by + * itself (two interior nodes can never be successive). Therefore, + * we can simply swap the node with its successor (including color) + * and have reduced this case to either of the first two. + * + * Whenever the node we removed was black, we have to rebalance the + * tree. Note that this affects the actual node we _remove_, not @n (in + * case we swap it). + * + * p: parent + * s: successor + * gc: grand-...-child + * x: temporary + * next: next node to rebalance on + */ + + if (!n->left) { + /* + * Case 1: + * The node has no left child. If it neither has a right child, + * it is a leaf-node and we can simply unlink it. If it also + * was black, we have to rebalance, as always if we remove a + * black node. + * But if the node has a right child, the child *must* be red + * (otherwise, the right path has more black nodes as the + * non-existing left path), and the node to be removed must + * hence be black. We simply replace the node with its child, + * turning the red child black, and thus no rebalancing is + * required. + */ + p = c_rbnode_parent(n); + c = c_rbnode_color(n); + c_rbtree_swap_child(t, p, n, n->right); + if (n->right) + c_rbnode_set_parent_and_color(n->right, p, c); + else + next = (c == C_RBNODE_BLACK) ? p : NULL; + } else if (!n->right) { + /* + * Case 1.1: + * The node has exactly one child, and it is on the left. Treat + * it as mirrored case of Case 1 (i.e., replace the node by its + * child). + */ + p = c_rbnode_parent(n); + c = c_rbnode_color(n); + c_rbtree_swap_child(t, p, n, n->left); + c_rbnode_set_parent_and_color(n->left, p, c); + } else { + /* + * Case 2: + * We are dealing with a full interior node with a child not on + * both sides. Find its successor and swap it. Then remove the + * node similar to Case 1. For performance reasons we don't + * perform the full swap, but skip links that are about to be + * removed, anyway. + */ + s = n->right; + if (!s->left) { + /* right child is next, no need to touch grandchild */ + p = s; + gc = s->right; + } else { + /* find successor and swap partially */ + s = c_rbnode_leftmost(s); + p = c_rbnode_parent(s); + + gc = s->right; + p->left = s->right; + s->right = n->right; + c_rbnode_set_parent(n->right, s); + } + + /* node is partially swapped, now remove as in Case 1 */ + s->left = n->left; + c_rbnode_set_parent(n->left, s); + + x = c_rbnode_parent(n); + c = c_rbnode_color(n); + c_rbtree_swap_child(t, x, n, s); + if (gc) + c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK); + else + next = c_rbnode_is_black(s) ? p : NULL; + c_rbnode_set_parent_and_color(s, x, c); + } + + if (next) + c_rbtree_rebalance(t, next); +} diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h new file mode 100644 index 0000000000..20c5515ca1 --- /dev/null +++ b/src/basic/c-rbtree.h @@ -0,0 +1,297 @@ +#pragma once + +/*** + This file is part of systemd. See COPYING for details. + + 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/>. +***/ + +/* + * Standalone Red-Black-Tree Implementation in Standard ISO-C11 + * + * This header provides an RB-Tree API, that is fully implemented in ISO-C11 + * and has no external dependencies. Furthermore, tree traversal, memory + * allocations, and key comparisons a fully in control of the API user. The + * implementation only provides the RB-Tree specific rebalancing and coloring. + * + * A tree is represented by the "CRBTree" structure. It contains a *singly* + * field, which is a pointer to the root node. If NULL, the tree is empty. If + * non-NULL, there is at least a single element in the tree. + * + * Each node of the tree is represented by the "CRBNode" structure. It has + * three fields. The @left and @right members can be accessed by the API user + * directly to traverse the tree. The third member is an implementation detail + * and encodes the parent pointer and color of the node. + * API users are required to embed the CRBNode object into their own objects + * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode + * pointers into pointers to their own structure. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CRBNode CRBNode; +typedef struct CRBTree CRBTree; + +/** + * struct CRBNode - Node of a Red-Black Tree + * @__parent_and_color: internal state + * @left: left child, or NULL + * @right: right child, or NULL + * + * Each node in an RB-Tree must embed an CRBNode object. This object contains + * pointers to its left and right child, which can be freely accessed by the + * API user at any time. They are NULL, if the node does not have a left/right + * child. + * + * The @__parent_and_color field must never be accessed directly. It encodes + * the pointer to the parent node, and the color of the node. Use the accessor + * functions instead. + * + * There is no reason to initialize a CRBNode object before linking it. + * However, if you need a boolean state that tells you whether the node is + * linked or not, you should initialize the node via c_rbnode_init() or + * C_RBNODE_INIT. + */ +struct CRBNode { + CRBNode *__parent_and_color; + CRBNode *left; + CRBNode *right; +}; + +#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) } + +CRBNode *c_rbnode_leftmost(CRBNode *n); +CRBNode *c_rbnode_rightmost(CRBNode *n); +CRBNode *c_rbnode_next(CRBNode *n); +CRBNode *c_rbnode_prev(CRBNode *n); + +/** + * struct CRBTree - Red-Black Tree + * @root: pointer to the root node, or NULL + * + * Each Red-Black Tree is rooted in an CRBTree object. This object contains a + * pointer to the root node of the tree. The API user is free to access the + * @root member at any time, and use it to traverse the tree. + * + * To initialize an RB-Tree, set it to NULL / all zero. + */ +struct CRBTree { + CRBNode *root; +}; + +CRBNode *c_rbtree_first(CRBTree *t); +CRBNode *c_rbtree_last(CRBTree *t); + +void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n); +void c_rbtree_remove(CRBTree *t, CRBNode *n); + +/** + * c_rbnode_init() - mark a node as unlinked + * @n: node to operate on + * + * This marks the node @n as unlinked. The node will be set to a valid state + * that can never happen if the node is linked in a tree. Furthermore, this + * state is fully known to the implementation, and as such handled gracefully + * in all cases. + * + * You are *NOT* required to call this on your node. c_rbtree_add() can handle + * uninitialized nodes just fine. However, calling this allows to use + * c_rbnode_is_linked() to check for the state of a node. Furthermore, + * iterators and accessors can be called on initialized (yet unlinked) nodes. + * + * Use the C_RBNODE_INIT macro if you want to initialize static variables. + */ +static inline void c_rbnode_init(CRBNode *n) { + *n = (CRBNode)C_RBNODE_INIT(*n); +} + +/** + * c_rbnode_is_linked() - check whether a node is linked + * @n: node to check, or NULL + * + * This checks whether the passed node is linked. If you pass NULL, or if the + * node is not linked into a tree, this will return false. Otherwise, this + * returns true. + * + * Note that you must have either linked the node or initialized it, before + * calling this function. Never call this function on uninitialized nodes. + * Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node + * as unlinked. You have to call c_rbnode_init() yourself after removal, or use + * the c_rbtree_remove_init() helper. + * + * Return: true if the node is linked, false if not. + */ +static inline _Bool c_rbnode_is_linked(CRBNode *n) { + return n && n->__parent_and_color != n; +} + +/** + * c_rbnode_parent() - return parent pointer + * @n node to access + * + * This returns a pointer to the parent of the given node @n. If @n does not + * have a parent, NULL is returned. If @n is not linked, @n itself is returned. + * + * You should not call this on unlinked or uninitialized nodes! If you do, you + * better know how its semantics. + * + * Return: Pointer to parent. + */ +static inline CRBNode *c_rbnode_parent(CRBNode *n) { + return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL); +} + +/** + * c_rbtree_remove_init() - safely remove node from tree and reinitialize it + * @t: tree to operate on + * @n: node to remove, or NULL + * + * This is almost the same as c_rbtree_remove(), but extends it slightly, to be + * more convenient to use in many cases: + * - if @n is unlinked or NULL, this is a no-op + * - @n is reinitialized after being removed + */ +static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) { + if (c_rbnode_is_linked(n)) { + c_rbtree_remove(t, n); + c_rbnode_init(n); + } +} + +/** + * CRBCompareFunc - compare a node to a key + * @t: tree where the node is linked to + * @k: key to compare + * @n: node to compare + * + * If you use the tree-traversal helpers (which are optional), you need to + * provide this callback so they can compare nodes in a tree to the key you + * look for. + * + * The tree @t is provided as optional context to this callback. The key you + * look for is provided as @k, the current node that should be compared to is + * provided as @n. This function should work like strcmp(), that is, return -1 + * if @key orders before @n, 0 if both compare equal, and 1 if it orders after + * @n. + */ +typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n); + +/** + * c_rbtree_find_node() - find node + * @t: tree to search through + * @f: comparison function + * @k: key to search for + * + * This searches through @t for a node that compares equal to @k. The function + * @f must be provided by the caller, which is used to compare nodes to @k. See + * the documentation of CRBCompareFunc for details. + * + * If there are multiple entries that compare equal to @k, this will return a + * pseudo-randomly picked node. If you need stable lookup functions for trees + * where duplicate entries are allowed, you better code your own lookup. + * + * Return: Pointer to matching node, or NULL. + */ +static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) { + CRBNode *i; + + assert(t); + assert(f); + + i = t->root; + while (i) { + int v = f(t, (void *)k, i); + if (v < 0) + i = i->left; + else if (v > 0) + i = i->right; + else + return i; + } + + return NULL; +} + +/** + * c_rbtree_find_entry() - find entry + * @_t: tree to search through + * @_f: comparison function + * @_k: key to search for + * @_t: type of the structure that embeds the nodes + * @_o: name of the node-member in type @_t + * + * This is very similar to c_rbtree_find_node(), but instead of returning a + * pointer to the CRBNode, it returns a pointer to the surrounding object. This + * object must embed the CRBNode object. The type of the surrounding object + * must be given as @_t, and the name of the embedded CRBNode member as @_o. + * + * See c_rbtree_find_node() for more details. + * + * Return: Pointer to found entry, NULL if not found. + */ +#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \ + ((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \ + (char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o))) + +/** + * c_rbtree_find_slot() - find slot to insert new node + * @t: tree to search through + * @f: comparison function + * @k: key to search for + * @p: output storage for parent pointer + * + * This searches through @t just like c_rbtree_find_node() does. However, + * instead of returning a pointer to a node that compares equal to @k, this + * searches for a slot to insert a node with key @k. A pointer to the slot is + * returned, and a pointer to the parent of the slot is stored in @p. Both + * can be passed directly to c_rbtree_add(), together with your node to insert. + * + * If there already is a node in the tree, that compares equal to @k, this will + * return NULL and store the conflicting node in @p. In all other cases, + * this will return a pointer (non-NULL) to the empty slot to insert the node + * at. @p will point to the parent node of that slot. + * + * If you want trees that allow duplicate nodes, you better code your own + * insertion function. + * + * Return: Pointer to slot to insert node, or NULL on conflicts. + */ +static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) { + CRBNode **i; + + assert(t); + assert(f); + assert(p); + + i = &t->root; + *p = NULL; + while (*i) { + int v = f(t, (void *)k, *i); + *p = *i; + if (v < 0) + i = &(*i)->left; + else if (v > 0) + i = &(*i)->right; + else + return NULL; + } + + return i; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 2c670a1ac6..8f83d9c142 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -30,8 +30,8 @@ #include "alloc-util.h" #include "calendarspec.h" #include "fileio.h" -#include "parse-util.h" #include "macro.h" +#include "parse-util.h" #include "string-util.h" #define BITS_WEEKDAYS 127 diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index aac812dc52..0e5cc452b9 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -23,9 +23,9 @@ #include <string.h> #include "cap-list.h" +#include "macro.h" #include "missing.h" #include "parse-util.h" -#include "macro.h" #include "util.h" static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 7c580caa43..639f9f3db1 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -34,14 +34,17 @@ #include "alloc-util.h" #include "cgroup-util.h" +#include "def.h" #include "dirent-util.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "fs-util.h" +#include "log.h" #include "login-util.h" #include "macro.h" +#include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "path-util.h" @@ -49,9 +52,6 @@ #include "process-util.h" #include "set.h" #include "special.h" -#include "def.h" -#include "log.h" -#include "missing.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" @@ -2135,7 +2135,7 @@ int cg_unified(void) { else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) unified_cache = false; else - return -ENOEXEC; + return -ENOMEDIUM; return unified_cache; } diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index b506995794..661785784a 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -28,9 +28,9 @@ #include <sys/types.h> #include "def.h" -#include "set.h" #include "hashmap.h" #include "macro.h" +#include "set.h" /* An enum of well known cgroup controllers */ typedef enum CGroupController { diff --git a/src/basic/copy.c b/src/basic/copy.c index c335959b74..024712d290 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -42,9 +42,9 @@ #include "fs-util.h" #include "io-util.h" #include "macro.h" -#include "time-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "umask-util.h" #include "xattr-util.h" diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c index 51c0693954..85b7519953 100644 --- a/src/basic/cpu-set-util.c +++ b/src/basic/cpu-set-util.c @@ -27,9 +27,9 @@ #include "alloc-util.h" #include "cpu-set-util.h" #include "extract-word.h" -#include "parse-util.h" #include "log.h" #include "macro.h" +#include "parse-util.h" #include "string-util.h" cpu_set_t* cpu_set_malloc(unsigned *ncpus) { diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 58273bb988..1ad5e4715a 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -25,8 +25,8 @@ #include <errno.h> #include <stdbool.h> -#include "path-util.h" #include "macro.h" +#include "path-util.h" int dirent_ensure_type(DIR *d, struct dirent *de); diff --git a/src/basic/env-util.c b/src/basic/env-util.c index fe8c825f36..dd56545f12 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -28,9 +28,9 @@ #include "alloc-util.h" #include "env-util.h" -#include "parse-util.h" #include "extract-word.h" #include "macro.h" +#include "parse-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h index 850f58fd98..664222c1d6 100644 --- a/src/basic/exit-status.h +++ b/src/basic/exit-status.h @@ -23,9 +23,9 @@ #include <stdbool.h> -#include "set.h" #include "hashmap.h" #include "macro.h" +#include "set.h" typedef enum ExitStatus { /* EXIT_SUCCESS defined by libc */ diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 678ac3b195..9759cac23c 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -27,11 +27,11 @@ #include <unistd.h> #include "fd-util.h" -#include "parse-util.h" -#include "socket-util.h" #include "macro.h" #include "missing.h" +#include "parse-util.h" #include "path-util.h" +#include "socket-util.h" #include "util.h" int close_nointr(int fd) { diff --git a/src/basic/fdset.c b/src/basic/fdset.c index 654ec5a639..de9b723ab8 100644 --- a/src/basic/fdset.c +++ b/src/basic/fdset.c @@ -29,11 +29,11 @@ #include "fd-util.h" #include "fdset.h" +#include "log.h" #include "macro.h" #include "parse-util.h" -#include "set.h" -#include "log.h" #include "path-util.h" +#include "set.h" #define MAKE_SET(s) ((Set*) s) #define MAKE_FDSET(s) ((FDSet*) s) diff --git a/src/basic/fdset.h b/src/basic/fdset.h index 58a5b45f28..615ba05661 100644 --- a/src/basic/fdset.h +++ b/src/basic/fdset.h @@ -23,9 +23,9 @@ #include <stdbool.h> -#include "set.h" #include "hashmap.h" #include "macro.h" +#include "set.h" typedef struct FDSet FDSet; diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c index 52a1515cd7..1cee87c9cd 100644 --- a/src/basic/fileio-label.c +++ b/src/basic/fileio-label.c @@ -23,8 +23,8 @@ #include <sys/stat.h> #include "fileio-label.h" -#include "selinux-util.h" #include "fileio.h" +#include "selinux-util.h" int write_string_file_atomic_label(const char *fn, const char *line) { int r; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 684ce3d58f..3a237252b5 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -37,15 +37,15 @@ #include "fileio.h" #include "fs-util.h" #include "hexdecoct.h" +#include "log.h" +#include "macro.h" #include "parse-util.h" #include "path-util.h" #include "random-util.h" -#include "log.h" -#include "macro.h" -#include "time-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "umask-util.h" #include "utf8.h" diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index cd7abee989..fb760abe18 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -34,15 +34,15 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" #include "log.h" #include "macro.h" #include "missing.h" -#include "time-util.h" +#include "mkdir.h" +#include "parse-util.h" +#include "path-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "user-util.h" #include "util.h" diff --git a/src/basic/label.c b/src/basic/label.c index 70e6ee20bf..f72a985967 100644 --- a/src/basic/label.c +++ b/src/basic/label.c @@ -24,9 +24,9 @@ #include <unistd.h> #include "label.h" +#include "macro.h" #include "selinux-util.h" #include "smack-util.h" -#include "macro.h" int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { int r, q; diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index 708da0d304..7784d02168 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -34,10 +34,10 @@ #include "dirent-util.h" #include "fd-util.h" +#include "hashmap.h" #include "locale-util.h" #include "path-util.h" #include "set.h" -#include "hashmap.h" #include "string-table.h" #include "string-util.h" #include "strv.h" diff --git a/src/basic/lockfile-util.c b/src/basic/lockfile-util.c index 704ae6cc52..6ecfc2ec46 100644 --- a/src/basic/lockfile-util.c +++ b/src/basic/lockfile-util.c @@ -30,8 +30,8 @@ #include "fd-util.h" #include "fs-util.h" #include "lockfile-util.h" -#include "path-util.h" #include "macro.h" +#include "path-util.h" int make_lock_file(const char *p, int operation, LockFile *ret) { _cleanup_close_ int fd = -1; diff --git a/src/basic/log.c b/src/basic/log.c index 829f85a5d8..1a9e6bdb91 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -49,12 +49,12 @@ #include "process-util.h" #include "signal-util.h" #include "socket-util.h" -#include "time-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" +#include "time-util.h" #include "util.h" #define SNDBUF_SIZE (8*1024*1024) diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c index a9b2151195..789638f013 100644 --- a/src/basic/memfd-util.c +++ b/src/basic/memfd-util.c @@ -32,9 +32,9 @@ #include "alloc-util.h" #include "fd-util.h" +#include "macro.h" #include "memfd-util.h" #include "missing.h" -#include "macro.h" #include "string-util.h" #include "utf8.h" diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 4b809541b1..9f9d52b5df 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -25,9 +25,9 @@ #include <sys/stat.h> #include "fs-util.h" +#include "macro.h" #include "mkdir.h" #include "path-util.h" -#include "macro.h" #include "stat-util.h" #include "user-util.h" diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index aaac2d47bd..10a6536cfc 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -31,11 +31,11 @@ #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "hashmap.h" #include "mount-util.h" #include "parse-util.h" #include "path-util.h" #include "set.h" -#include "hashmap.h" #include "stdio-util.h" #include "string-util.h" diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h index 3be75e6614..b37250f08e 100644 --- a/src/basic/mount-util.h +++ b/src/basic/mount-util.h @@ -28,8 +28,8 @@ #include <sys/stat.h> #include <sys/types.h> -#include "missing.h" #include "macro.h" +#include "missing.h" int fd_is_mount_point(int fd, const char *filename, int flags); int path_is_mount_point(const char *path, int flags); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 3d8123ca0d..618ef5d564 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -29,8 +29,8 @@ #include "alloc-util.h" #include "extract-word.h" -#include "parse-util.h" #include "macro.h" +#include "parse-util.h" #include "string-util.h" int parse_boolean(const char *v) { diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 95b1052aeb..61fab0e087 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -34,16 +34,16 @@ #undef basename #include "alloc-util.h" +#include "extract-word.h" #include "fs-util.h" #include "log.h" #include "macro.h" #include "missing.h" #include "path-util.h" -#include "extract-word.h" -#include "time-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" bool path_is_absolute(const char *p) { return p[0] == '/'; diff --git a/src/basic/prioq.c b/src/basic/prioq.c index 7d420d8a7b..86c5c0e9b4 100644 --- a/src/basic/prioq.c +++ b/src/basic/prioq.c @@ -33,8 +33,8 @@ #include <stdlib.h> #include "alloc-util.h" -#include "prioq.h" #include "hashmap.h" +#include "prioq.h" struct prioq_item { void *data; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 1d60811cbf..4cc54a51fb 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -41,10 +41,10 @@ #include "fs-util.h" #include "ioprio.h" #include "log.h" -#include "process-util.h" -#include "signal-util.h" #include "macro.h" #include "missing.h" +#include "process-util.h" +#include "signal-util.h" #include "string-table.h" #include "string-util.h" #include "user-util.h" diff --git a/src/basic/ratelimit.c b/src/basic/ratelimit.c index ee0f8176b9..b62f3da76b 100644 --- a/src/basic/ratelimit.c +++ b/src/basic/ratelimit.c @@ -22,8 +22,8 @@ #include <sys/time.h> -#include "ratelimit.h" #include "macro.h" +#include "ratelimit.h" /* Modelled after Linux' lib/ratelimit.c by Dave Young * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */ diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index 2de965daa6..44f885db16 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -22,9 +22,9 @@ #include <errno.h> #include <sys/resource.h> +#include "macro.h" #include "missing.h" #include "rlimit-util.h" -#include "macro.h" #include "string-table.h" int setrlimit_closest(int resource, const struct rlimit *rlim) { diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index 0408e22777..14f8474da0 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -30,11 +30,11 @@ #include "btrfs-util.h" #include "fd-util.h" +#include "log.h" +#include "macro.h" #include "mount-util.h" #include "path-util.h" #include "rm-rf.h" -#include "log.h" -#include "macro.h" #include "stat-util.h" #include "string-util.h" diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 9be8e2c76f..5956c4fe43 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -35,10 +35,10 @@ #endif #include "alloc-util.h" -#include "path-util.h" -#include "selinux-util.h" #include "log.h" #include "macro.h" +#include "path-util.h" +#include "selinux-util.h" #include "time-util.h" #include "util.h" diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index fd9258dfca..7637fccb2f 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -23,9 +23,9 @@ #include <stdarg.h> #include <stdio.h> +#include "macro.h" #include "parse-util.h" #include "signal-util.h" -#include "macro.h" #include "string-table.h" #include "string-util.h" diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 65667b9859..060e8ba387 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -17,8 +17,8 @@ coding style) */ -#include "siphash24.h" #include "macro.h" +#include "siphash24.h" #include "unaligned.h" static inline uint64_t rotate_left(uint64_t x, uint8_t b) { diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c index e8030c92f3..b9e4ff87d8 100644 --- a/src/basic/smack-util.c +++ b/src/basic/smack-util.c @@ -29,11 +29,11 @@ #include "alloc-util.h" #include "fileio.h" +#include "log.h" +#include "macro.h" #include "path-util.h" #include "process-util.h" #include "smack-util.h" -#include "log.h" -#include "macro.h" #include "string-table.h" #include "xattr-util.h" diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 2dc6c76752..e169439e04 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -31,12 +31,12 @@ #include "alloc-util.h" #include "fd-util.h" +#include "log.h" #include "macro.h" #include "missing.h" #include "mkdir.h" #include "selinux-util.h" #include "socket-util.h" -#include "log.h" int socket_address_listen( const SocketAddress *a, diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 240fb60212..79901a6a06 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -36,12 +36,12 @@ #include "fd-util.h" #include "fileio.h" #include "formats-util.h" +#include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" -#include "log.h" #include "string-table.h" #include "string-util.h" #include "user-util.h" @@ -870,16 +870,24 @@ int getpeersec(int fd, char **ret) { return 0; } -int send_one_fd(int transport_fd, int fd, int flags) { +int send_one_fd_sa( + int transport_fd, + int fd, + const struct sockaddr *sa, socklen_t len, + int flags) { + union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; } control = {}; + struct cmsghdr *cmsg; + struct msghdr mh = { + .msg_name = (struct sockaddr*) sa, + .msg_namelen = len, .msg_control = &control, .msg_controllen = sizeof(control), }; - struct cmsghdr *cmsg; assert(transport_fd >= 0); assert(fd >= 0); diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index f9c90e0e73..6da1df68d8 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -128,7 +128,11 @@ int ip_tos_from_string(const char *s); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); -int send_one_fd(int transport_fd, int fd, int flags); +int send_one_fd_sa(int transport_fd, + int fd, + const struct sockaddr *sa, socklen_t len, + int flags); +#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags) int receive_one_fd(int transport_fd, int flags); #define CMSG_FOREACH(cmsg, mh) \ diff --git a/src/basic/string-table.c b/src/basic/string-table.c index 07a6d785f8..4633a57f44 100644 --- a/src/basic/string-table.c +++ b/src/basic/string-table.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "string-util.h" #include "string-table.h" +#include "string-util.h" ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { size_t i; diff --git a/src/basic/strv.h b/src/basic/strv.h index d0dd08baf3..bb61db2638 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -26,8 +26,8 @@ #include <stdbool.h> #include <stddef.h> -#include "extract-word.h" #include "alloc-util.h" +#include "extract-word.h" #include "macro.h" #include "util.h" diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 68664a152f..a39764472b 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -43,11 +43,11 @@ #include "fileio.h" #include "fs-util.h" #include "io-util.h" +#include "log.h" +#include "macro.h" #include "parse-util.h" #include "process-util.h" #include "socket-util.h" -#include "log.h" -#include "macro.h" #include "stat-util.h" #include "string-util.h" #include "terminal-util.h" diff --git a/src/basic/time-util.c b/src/basic/time-util.c index a9296d6ee6..bfc7cf870c 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -34,10 +34,10 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "parse-util.h" -#include "path-util.h" #include "log.h" #include "macro.h" +#include "parse-util.h" +#include "path-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index bdec97e54b..5fc3b9d6fd 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -28,8 +28,8 @@ #include "alloc-util.h" #include "bus-label.h" #include "hexdecoct.h" -#include "path-util.h" #include "macro.h" +#include "path-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 55c64abdd9..56e1a3be48 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -34,10 +34,10 @@ #include "alloc-util.h" #include "fd-util.h" +#include "formats-util.h" #include "macro.h" #include "parse-util.h" #include "path-util.h" -#include "formats-util.h" #include "string-util.h" #include "user-util.h" diff --git a/src/basic/util.c b/src/basic/util.c index 6d264e0007..9e0b576283 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -51,12 +51,12 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" -#include "signal-util.h" #include "set.h" -#include "time-util.h" +#include "signal-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "user-util.h" #include "util.h" diff --git a/src/basic/virt.c b/src/basic/virt.c index eb67949166..0ffc2769d2 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -26,9 +26,11 @@ #include <unistd.h> #include "alloc-util.h" +#include "dirent-util.h" +#include "fd-util.h" #include "fileio.h" -#include "process-util.h" #include "macro.h" +#include "process-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 166e2b23fa..960209282f 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -29,10 +29,10 @@ #include "alloc-util.h" #include "fd-util.h" -#include "sparse-endian.h" #include "macro.h" -#include "time-util.h" +#include "sparse-endian.h" #include "stdio-util.h" +#include "time-util.h" #include "xattr-util.h" int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) { diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index e6277a9084..12537ae85b 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -131,7 +131,7 @@ static int get_cgroup_root(char **ret) { if (!arg_machine) { r = cg_get_root_path(ret); - if (r == -ENOEXEC) + if (r == -ENOMEDIUM) return log_error_errno(r, "Failed to get root control group path: No cgroup filesystem mounted on /sys/fs/cgroup"); else if (r < 0) return log_error_errno(r, "Failed to get root control group path: %m"); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 093179c003..1f736b2686 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -632,21 +632,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), diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 5457b2451b..61bd0f8d5f 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1939,21 +1939,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), diff --git a/src/core/execute.c b/src/core/execute.c index 4f67a9de83..9b76861919 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2413,9 +2413,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; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 05655fc99a..3f3c5bf9fc 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -134,52 +134,45 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { #endif va_start(ap, fmt); - log_internalv(LOG_AUTH | callback_type_to_priority(type), - 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap); + log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap); va_end(ap); return 0; } -/* - Function must be called once to initialize the SELinux AVC environment. - Sets up callbacks. - If you want to cleanup memory you should need to call selinux_access_finish. -*/ -static int access_init(void) { - int r = 0; +static int access_init(sd_bus_error *error) { - if (avc_open(NULL, 0)) - return log_error_errno(errno, "avc_open() failed: %m"); - - selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); - selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); + if (!mac_selinux_use()) + return 0; - if (security_getenforce() < 0){ - r = -errno; - avc_destroy(); - } + if (initialized) + return 1; - return r; -} + if (avc_open(NULL, 0) != 0) { + int enforce, saved_errno = errno; -static int mac_selinux_access_init(sd_bus_error *error) { - int r; + enforce = security_getenforce(); + log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m"); - if (initialized) - return 0; + /* If enforcement isn't on, then let's suppress this + * error, and just don't do any AVC checks. The + * warning we printed is hence all the admin will + * see. */ + if (enforce == 0) + return 0; - if (!mac_selinux_use()) - return 0; + /* Return an access denied error, if we couldn't load + * the AVC but enforcing mode was on, or we couldn't + * determine whether it is one. */ + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno)); + } - r = access_init(); - if (r < 0) - return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux."); + selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); initialized = true; - return 0; + return 1; } -#endif /* This function communicates with the kernel to check whether or not it should @@ -193,7 +186,6 @@ int mac_selinux_generic_access_check( const char *permission, sd_bus_error *error) { -#ifdef HAVE_SELINUX _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *tclass = NULL, *scon = NULL; struct audit_info audit_info = {}; @@ -206,11 +198,8 @@ int mac_selinux_generic_access_check( assert(permission); assert(error); - if (!mac_selinux_use()) - return 0; - - r = mac_selinux_access_init(error); - if (r < 0) + r = access_init(error); + if (r <= 0) return r; r = sd_bus_query_sender_creds( @@ -277,7 +266,17 @@ finish: } return r; +} + #else + +int mac_selinux_generic_access_check( + sd_bus_message *message, + const char *path, + const char *permission, + sd_bus_error *error) { + return 0; -#endif } + +#endif diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index e1fbacd11b..d383041d39 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -212,10 +212,10 @@ try_dmi: unreliable enough, so let's not do any additional guesswork on top of that. - See the SMBIOS Specification 2.7.1 section 7.4.1 for + See the SMBIOS Specification 4.0 section 7.4.1 for details about the values listed here: - http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf + https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf */ switch (t) { @@ -237,7 +237,11 @@ try_dmi: case 0x11: case 0x1C: + case 0x1D: return "server"; + + case 0x1E: + return "tablet"; } return NULL; diff --git a/src/journal/compress.c b/src/journal/compress.c index e1ca0a8818..1a3d2cdd80 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -201,7 +201,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size, return -EBADMSG; size = le64toh( *(le64_t*)src ); - if (size < 0 || (le64_t) size != *(le64_t*)src) + if (size < 0 || (unsigned) size != le64toh(*(le64_t*)src)) return -EFBIG; if ((size_t) size > *dst_alloc_size) { out = realloc(*dst, size); diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index fa5dee73c3..44fa11a00e 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -225,8 +225,8 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { assert_return(iov, -EINVAL); assert_return(n > 0, -EINVAL); - w = alloca(sizeof(struct iovec) * n * 5 + 3); - l = alloca(sizeof(uint64_t) * n); + w = newa(struct iovec, n * 5 + 3); + l = newa(uint64_t, n); for (i = 0; i < n; i++) { char *c, *nl; @@ -337,7 +337,11 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { return r; } - return send_one_fd(fd, buffer_fd, 0); + r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0); + if (r == -ENOENT) + /* Fail silently if the journal is not available */ + return 0; + return r; } static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) { diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c index 694376670d..e537c1fe5f 100644 --- a/src/journal/test-journal-send.c +++ b/src/journal/test-journal-send.c @@ -19,59 +19,84 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdlib.h> #include <unistd.h> #include "sd-journal.h" -#include "log.h" +#include "macro.h" int main(int argc, char *argv[]) { char huge[4096*1024]; - log_set_max_level(LOG_DEBUG); - - sd_journal_print(LOG_INFO, "piepapo"); - - sd_journal_send("MESSAGE=foobar", - "VALUE=%i", 7, - NULL); + /* utf-8 and non-utf-8, message-less and message-ful iovecs */ + struct iovec graph1[] = { + {(char*) "GRAPH=graph", strlen("GRAPH=graph")} + }; + struct iovec graph2[] = { + {(char*) "GRAPH=graph\n", strlen("GRAPH=graph\n")} + }; + struct iovec message1[] = { + {(char*) "MESSAGE=graph", strlen("MESSAGE=graph")} + }; + struct iovec message2[] = { + {(char*) "MESSAGE=graph\n", strlen("MESSAGE=graph\n")} + }; + + assert_se(sd_journal_print(LOG_INFO, "piepapo") == 0); + + assert_se(sd_journal_send("MESSAGE=foobar", + "VALUE=%i", 7, + NULL) == 0); errno = ENOENT; - sd_journal_perror("Foobar"); + assert_se(sd_journal_perror("Foobar") == 0); - sd_journal_perror(""); + assert_se(sd_journal_perror("") == 0); memset(huge, 'x', sizeof(huge)); memcpy(huge, "HUGE=", 5); char_array_0(huge); - sd_journal_send("MESSAGE=Huge field attached", - huge, - NULL); + assert_se(sd_journal_send("MESSAGE=Huge field attached", + huge, + NULL) == 0); - sd_journal_send("MESSAGE=uiui", - "VALUE=A", - "VALUE=B", - "VALUE=C", - "SINGLETON=1", - "OTHERVALUE=X", - "OTHERVALUE=Y", - "WITH_BINARY=this is a binary value \a", - NULL); + assert_se(sd_journal_send("MESSAGE=uiui", + "VALUE=A", + "VALUE=B", + "VALUE=C", + "SINGLETON=1", + "OTHERVALUE=X", + "OTHERVALUE=Y", + "WITH_BINARY=this is a binary value \a", + NULL) == 0); syslog(LOG_NOTICE, "Hello World!"); - sd_journal_print(LOG_NOTICE, "Hello World"); - - sd_journal_send("MESSAGE=Hello World!", - "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555", - "PRIORITY=5", - "HOME=%s", getenv("HOME"), - "TERM=%s", getenv("TERM"), - "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE), - "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN), - NULL); + assert_se(sd_journal_print(LOG_NOTICE, "Hello World") == 0); + + assert_se(sd_journal_send("MESSAGE=Hello World!", + "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555", + "PRIORITY=5", + "HOME=%s", getenv("HOME"), + "TERM=%s", getenv("TERM"), + "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE), + "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN), + NULL) == 0); + + assert_se(sd_journal_sendv(graph1, 1) == 0); + assert_se(sd_journal_sendv(graph2, 1) == 0); + assert_se(sd_journal_sendv(message1, 1) == 0); + assert_se(sd_journal_sendv(message2, 1) == 0); + + /* test without location fields */ +#undef sd_journal_sendv + assert_se(sd_journal_sendv(graph1, 1) == 0); + assert_se(sd_journal_sendv(graph2, 1) == 0); + assert_se(sd_journal_sendv(message1, 1) == 0); + assert_se(sd_journal_sendv(message2, 1) == 0); sleep(1); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index a03c8460a8..4521f8f0b1 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -554,7 +554,7 @@ static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t buffer[1] = 0; /* RCODE1 (deprecated) */ buffer[2] = 0; /* RCODE2 (deprecated) */ - r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3); + r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false); if (r > 0) r = dhcp_option_append(message, optlen, optoffset, 0, DHCP_OPTION_FQDN, 3 + r, buffer); diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 500fffc5ce..8003501059 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; int r; - if (cg_unified() == -ENOEXEC) { + if (cg_unified() == -ENOMEDIUM) { puts("Skipping test: /sys/fs/cgroup/ not available"); return EXIT_TEST_SKIP; } diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c index 2d3e62410c..2aae0726c1 100644 --- a/src/libudev/libudev-device-private.c +++ b/src/libudev/libudev-device-private.c @@ -137,14 +137,10 @@ gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) { } void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) { - sd_device *device_old = NULL; - assert(udev_device); - if (udev_device_old) - device_old = udev_device_old->device; - - device_ensure_usec_initialized(udev_device->device, device_old); + device_ensure_usec_initialized(udev_device->device, + udev_device_old ? udev_device_old->device : NULL); } char **udev_device_get_properties_envp(struct udev_device *udev_device) { diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index d0875cf930..c1643cf41a 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1820,7 +1820,7 @@ static int nologin_timeout_handler( log_info("Creating /run/nologin, blocking further logins..."); - r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + r = write_string_file_atomic_label("/run/nologin", "System is going down."); if (r < 0) log_error_errno(r, "Failed to create /run/nologin: %m"); else diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 49e574b6ca..c6b5b1ec44 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -1325,6 +1325,10 @@ int manager_start_scope( if (r < 0) return r; + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192); + if (r < 0) + return bus_log_create_error(r); + if (more_properties) { r = sd_bus_message_copy(m, more_properties, true); if (r < 0) diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index d6c0200c0c..65ca9c762b 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -105,10 +105,6 @@ int register_machine( return bus_log_create_error(r); } - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict"); if (r < 0) return bus_log_create_error(r); diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 36dfc70e00..0f154d9798 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -67,6 +67,8 @@ static void print_source(uint64_t flags, usec_t rtt) { fputc('.', stdout); fputc('\n', stdout); + + printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED)); } static int resolve_host(sd_bus *bus, const char *name) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index f86c4ceb05..0ceca56371 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -197,7 +197,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { r = sd_bus_message_append( reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), - SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; @@ -344,7 +344,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; @@ -425,8 +425,6 @@ fail: } static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - size_t start; int r; assert(m); @@ -443,17 +441,11 @@ static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int i if (r < 0) return r; - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + r = dns_resource_record_to_wire_format(rr, false); if (r < 0) return r; - p->refuse_compression = true; - - r = dns_packet_append_rr(p, rr, &start); - if (r < 0) - return r; - - r = sd_bus_message_append_array(m, 'y', DNS_PACKET_DATA(p) + start, p->size - start); + r = sd_bus_message_append_array(m, 'y', rr->wire_format, rr->wire_format_size); if (r < 0) return r; @@ -518,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; @@ -867,7 +859,7 @@ static void resolve_service_all_complete(DnsQuery *q) { reply, "ssst", name, type, domain, - SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 3fc7d9ae3d..1b2f3e336e 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -234,6 +234,41 @@ int config_parse_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; diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 28d2549d35..668ea02bba 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -36,3 +36,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); diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index be29f51663..6014d345f3 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -24,10 +24,15 @@ #define SD_RESOLVED_DNS (UINT64_C(1) << 0) #define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) #define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) +#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3) +#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4) #define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) #define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) #define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) #define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8) +#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9) #define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) -#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) +#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6) + +#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 8814919deb..89c254b02e 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -24,6 +24,7 @@ typedef struct DnsAnswer DnsAnswer; typedef struct DnsAnswerItem DnsAnswerItem; +#include "macro.h" #include "resolved-dns-rr.h" /* A simple array of resource records. We keep track of the @@ -59,19 +60,23 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); -#define DNS_ANSWER_FOREACH(kk, a) \ - for (unsigned _i = ({ \ +#define _DNS_ANSWER_FOREACH(q, kk, a) \ + for (unsigned UNIQ_T(i, q) = ({ \ (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \ 0; \ - }); \ - (a) && ((_i) < (a)->n_rrs); \ - _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL)) + }); \ + (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)) -#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) \ - for (unsigned _i = ({ \ +#define DNS_ANSWER_FOREACH(kk, a) _DNS_ANSWER_FOREACH(UNIQ, kk, a) + +#define _DNS_ANSWER_FOREACH_IFINDEX(q, kk, ifindex, 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; \ 0; \ - }); \ - (a) && ((_i) < (a)->n_rrs); \ - _i++, (kk) = ((_i < (a)->n_rrs) ? (a)->items[_i].rr : NULL), (ifindex) = ((_i < (a)->n_rrs) ? (a)->items[_i].ifindex : 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)) + +#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) _DNS_ANSWER_FOREACH_IFINDEX(UNIQ, kk, ifindex, a) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index d963ce6e00..1774ae6cb8 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -46,6 +46,7 @@ struct DnsCacheItem { usec_t until; DnsCacheItemType type; unsigned prioq_idx; + bool authenticated; int owner_family; union in_addr_union owner_address; LIST_FIELDS(DnsCacheItem, by_key); @@ -237,7 +238,7 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { return NULL; } -static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) { +static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) { assert(c); assert(i); assert(rr); @@ -257,6 +258,7 @@ 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->authenticated = authenticated; i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); prioq_reshuffle(c->by_expiry, i, &i->prioq_idx); @@ -265,6 +267,7 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso static int dns_cache_put_positive( DnsCache *c, DnsResourceRecord *rr, + bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address) { @@ -300,7 +303,7 @@ static int dns_cache_put_positive( /* Entry exists already? Update TTL and timestamp */ existing = dns_cache_get(c, rr); if (existing) { - dns_cache_item_update_positive(c, existing, rr, timestamp); + dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp); return 0; } @@ -322,6 +325,7 @@ static int dns_cache_put_positive( i->prioq_idx = PRIOQ_IDX_NULL; i->owner_family = owner_family; i->owner_address = *owner_address; + i->authenticated = authenticated; r = dns_cache_link_item(c, i); if (r < 0) @@ -341,6 +345,7 @@ static int dns_cache_put_negative( DnsCache *c, DnsResourceKey *key, int rcode, + bool authenticated, usec_t timestamp, uint32_t soa_ttl, int owner_family, @@ -389,6 +394,7 @@ static int dns_cache_put_negative( i->prioq_idx = PRIOQ_IDX_NULL; i->owner_family = owner_family; i->owner_address = *owner_address; + i->authenticated = authenticated; r = dns_cache_link_item(c, i); if (r < 0) @@ -410,6 +416,7 @@ int dns_cache_put( int rcode, DnsAnswer *answer, unsigned max_rrs, + bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address) { @@ -452,7 +459,12 @@ int dns_cache_put( /* Second, add in positive entries for all contained RRs */ for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address); + DnsResourceRecord *rr = answer->items[i].rr; + + if (rr->key->cache_flush) + dns_cache_remove(c, rr->key); + + r = dns_cache_put_positive(c, rr, authenticated, timestamp, owner_family, owner_address); if (r < 0) goto fail; } @@ -496,13 +508,13 @@ int dns_cache_put( if (!dns_answer_match_soa(canonical_key, soa->key)) continue; - r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); + 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; } } - r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); + r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); if (r < 0) goto fail; @@ -521,8 +533,7 @@ fail: return r; } -static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL; +static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) { DnsCacheItem *i; const char *n; int r; @@ -534,32 +545,29 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceK * much, after all this is just a cache */ i = hashmap_get(c->by_key, k); - if (i || k->type == DNS_TYPE_CNAME || k->type == DNS_TYPE_DNAME) + if (i || IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME, DNS_TYPE_NSEC)) return i; - /* Check if we have a CNAME record instead */ - cname_key = dns_resource_key_new_cname(k); - if (!cname_key) - return NULL; + n = DNS_RESOURCE_KEY_NAME(k); - i = hashmap_get(c->by_key, cname_key); + /* 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; + + /* 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. */ - n = DNS_RESOURCE_KEY_NAME(k); for (;;) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL; char label[DNS_LABEL_MAX]; if (isempty(n)) return NULL; - dname_key = dns_resource_key_new(k->class, DNS_TYPE_DNAME, n); - if (!dname_key) - return NULL; - - i = hashmap_get(c->by_key, dname_key); + i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n)); if (i) return i; @@ -572,23 +580,26 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceK return NULL; } -int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) { +int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; unsigned n = 0; int r; bool nxdomain = false; _cleanup_free_ char *key_str = NULL; - DnsCacheItem *j, *first; + DnsCacheItem *j, *first, *nsec = NULL; + bool have_authenticated = false, have_non_authenticated = false; assert(c); assert(key); assert(rcode); assert(ret); + assert(authenticated); if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { - /* If we have ANY lookups we simply refresh */ + /* 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) @@ -601,7 +612,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return 0; } - first = dns_cache_get_by_key_follow_cname(c, key); + first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key); if (!first) { /* If one question cannot be answered we need to refresh */ @@ -617,24 +628,49 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r } LIST_FOREACH(by_key, j, first) { - if (j->rr) + if (j->rr) { + if (j->rr->key->type == DNS_TYPE_NSEC) + nsec = j; + n++; - else if (j->type == DNS_CACHE_NXDOMAIN) + } else if (j->type == DNS_CACHE_NXDOMAIN) nxdomain = true; + + if (j->authenticated) + have_authenticated = true; + else + 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); + + /* We only found an NSEC record that matches our name. + * If it says the type doesn't exit 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); + } + log_debug("%s cache hit for %s", - nxdomain ? "NXDOMAIN" : - n > 0 ? "Positive" : "NODATA", + n > 0 ? "Positive" : + nxdomain ? "NXDOMAIN" : "NODATA", key_str); if (n <= 0) { *ret = NULL; *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS; + *authenticated = have_authenticated && !have_non_authenticated; return 1; } @@ -653,6 +689,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r *ret = answer; *rcode = DNS_RCODE_SUCCESS; + *authenticated = have_authenticated && !have_non_authenticated; answer = NULL; return n; @@ -694,6 +731,57 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ return 1; } +int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { + unsigned ancount = 0; + Iterator iterator; + DnsCacheItem *i; + 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)) + 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; + + ancount ++; + } + } + + DNS_PACKET_HEADER(p)->ancount = htobe16(ancount); + + return 0; +} + void dns_cache_dump(DnsCache *cache, FILE *f) { Iterator iterator; DnsCacheItem *i; diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 164435b4fb..0f28bbe543 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -21,7 +21,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ - #include "hashmap.h" #include "list.h" #include "prioq.h" @@ -33,16 +32,19 @@ typedef struct DnsCache { } DnsCache; #include "resolved-dns-answer.h" +#include "resolved-dns-packet.h" #include "resolved-dns-question.h" #include "resolved-dns-rr.h" 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, 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); +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_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); void dns_cache_dump(DnsCache *cache, FILE *f); bool dns_cache_is_empty(DnsCache *cache); + +int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c new file mode 100644 index 0000000000..2d06775dca --- /dev/null +++ b/src/resolve/resolved-dns-dnssec.c @@ -0,0 +1,719 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 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 <gcrypt.h> + +#include "alloc-util.h" +#include "dns-domain.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" +#include "string-table.h" + +/* Open question: + * + * How does the DNSSEC canonical form of a hostname with a label + * containing a dot look like, the way DNS-SD does it? + * + * TODO: + * + * - 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 + * - multi-label zone compatibility + * - DMSSEC cname/dname compatibility + * - per-interface DNSSEC setting + * - DSA support + * - EC support? + * + * */ + +#define VERIFY_RRS_MAX 256 +#define MAX_KEY_SIZE (32*1024) + +/* 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) + +/* + * The DNSSEC Chain of trust: + * + * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone + * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree + * DS RRs are protected like normal RRs + * + * Example chain: + * 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 bool dnssec_digest_supported(int digest) { + return IN_SET(digest, + DNSSEC_DIGEST_SHA1, + DNSSEC_DIGEST_SHA256); +} + +uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { + const uint8_t *p; + uint32_t sum; + size_t i; + + /* The algorithm from RFC 4034, Appendix B. */ + + 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); + + p = dnskey->dnskey.key; + + for (i = 0; i < dnskey->dnskey.key_size; i++) + sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i]; + + sum += (sum >> 16) & UINT32_C(0xFFFF); + + return sum & UINT32_C(0xFFFF); +} + +static int rr_compare(const void *a, const void *b) { + DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; + size_t m; + int r; + + /* Let's order the RRs according to RFC 4034, Section 6.3 */ + + assert(x); + assert(*x); + assert((*x)->wire_format); + assert(y); + assert(*y); + assert((*y)->wire_format); + + m = MIN((*x)->wire_format_size, (*y)->wire_format_size); + + r = memcmp((*x)->wire_format, (*y)->wire_format, m); + if (r != 0) + return r; + + if ((*x)->wire_format_size < (*y)->wire_format_size) + return -1; + else if ((*x)->wire_format_size > (*y)->wire_format_size) + return 1; + + return 0; +} + +static int dnssec_rsa_verify( + const char *hash_algorithm, + const void *signature, size_t signature_size, + const void *data, size_t data_size, + const void *exponent, size_t exponent_size, + const void *modulus, size_t modulus_size) { + + gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; + gcry_mpi_t n = NULL, e = NULL, s = NULL; + gcry_error_t ge; + int r; + + assert(hash_algorithm); + + ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL); + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL); + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL); + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&signature_sexp, + NULL, + "(sig-val (rsa (s %m)))", + s); + + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&data_sexp, + NULL, + "(data (flags pkcs1) (hash %s %b))", + hash_algorithm, + (int) data_size, + data); + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_sexp_build(&public_key_sexp, + NULL, + "(public-key (rsa (n %m) (e %m)))", + n, + e); + if (ge != 0) { + r = -EIO; + goto finish; + } + + ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp); + if (ge == GPG_ERR_BAD_SIGNATURE) + r = 0; + else if (ge != 0) + r = -EIO; + else + r = 1; + +finish: + if (e) + gcry_mpi_release(e); + if (n) + gcry_mpi_release(n); + if (s) + gcry_mpi_release(s); + + 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 r; +} + +static void md_add_uint8(gcry_md_hd_t md, uint8_t v) { + gcry_md_write(md, &v, sizeof(v)); +} + +static void md_add_uint16(gcry_md_hd_t md, uint16_t v) { + v = htobe16(v); + gcry_md_write(md, &v, sizeof(v)); +} + +static void md_add_uint32(gcry_md_hd_t md, uint32_t v) { + v = htobe32(v); + gcry_md_write(md, &v, sizeof(v)); +} + +static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { + usec_t expiration, inception, skew; + + assert(rrsig); + assert(rrsig->key->type == DNS_TYPE_RRSIG); + + if (realtime == USEC_INFINITY) + realtime = now(CLOCK_REALTIME); + + expiration = rrsig->rrsig.expiration * USEC_PER_SEC; + inception = rrsig->rrsig.inception * USEC_PER_SEC; + + if (inception > expiration) + return -EKEYREJECTED; + + /* Permit a certain amount of clock skew of 10% of the valid + * time range. This takes inspiration from unbound's + * resolver. */ + skew = (expiration - inception) / 10; + if (skew > SKEW_MAX) + skew = SKEW_MAX; + + if (inception < skew) + inception = 0; + else + inception -= skew; + + if (expiration + skew < expiration) + expiration = USEC_INFINITY; + else + expiration += skew; + + return realtime < inception || realtime > expiration; +} + +int dnssec_verify_rrset( + DnsAnswer *a, + DnsResourceKey *key, + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey, + usec_t realtime) { + + uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX]; + size_t exponent_size, modulus_size, hash_size; + void *exponent, *modulus, *hash; + DnsResourceRecord **list, *rr; + gcry_md_hd_t md = NULL; + size_t k, n = 0; + int r; + + assert(key); + assert(rrsig); + assert(dnskey); + assert(rrsig->key->type == DNS_TYPE_RRSIG); + assert(dnskey->key->type == DNS_TYPE_DNSKEY); + + /* Verifies the the RRSet matching the specified "key" in "a", + * 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; + + r = dnssec_rrsig_expired(rrsig, realtime); + if (r < 0) + return r; + if (r > 0) + return DNSSEC_SIGNATURE_EXPIRED; + + /* Collect all relevant RRs in a single array, so that we can look at the RRset */ + list = newa(DnsResourceRecord *, a->n_rrs); + + DNS_ANSWER_FOREACH(rr, a) { + r = dns_resource_key_equal(key, rr->key); + if (r < 0) + return r; + if (r == 0) + continue; + + /* We need the wire format for ordering, and digest calculation */ + r = dns_resource_record_to_wire_format(rr, true); + if (r < 0) + return r; + + list[n++] = rr; + } + + if (n <= 0) + return -ENODATA; + + /* Bring the RRs into canonical order */ + 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; + + 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"); + } + + if (!md) + return -EIO; + + md_add_uint16(md, rrsig->rrsig.type_covered); + md_add_uint8(md, rrsig->rrsig.algorithm); + md_add_uint8(md, rrsig->rrsig.labels); + md_add_uint32(md, rrsig->rrsig.original_ttl); + md_add_uint32(md, rrsig->rrsig.expiration); + md_add_uint32(md, rrsig->rrsig.inception); + md_add_uint16(md, rrsig->rrsig.key_tag); + + r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true); + if (r < 0) + goto finish; + gcry_md_write(md, wire_format_name, r); + + for (k = 0; k < n; k++) { + size_t l; + rr = list[k]; + + r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true); + if (r < 0) + goto finish; + gcry_md_write(md, wire_format_name, r); + + md_add_uint16(md, rr->key->type); + md_add_uint16(md, rr->key->class); + md_add_uint32(md, rrsig->rrsig.original_ttl); + + assert(rr->wire_format_rdata_offset <= rr->wire_format_size); + l = rr->wire_format_size - rr->wire_format_rdata_offset; + 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); + } + + hash = gcry_md_read(md, 0); + if (!hash) { + r = -EIO; + 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; + } + + if (1 + exponent_size >= dnskey->dnskey.key_size) { + r = -EINVAL; + goto finish; + } + + modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size; + modulus_size = dnskey->dnskey.key_size - 1 - exponent_size; + } + + r = dnssec_rsa_verify( + gcry_md_algo_name(gcry_md_get_algo(md)), + rrsig->rrsig.signature, rrsig->rrsig.signature_size, + hash, hash_size, + exponent, exponent_size, + modulus, modulus_size); + if (r < 0) + goto finish; + + r = r ? DNSSEC_VERIFIED : DNSSEC_INVALID; + +finish: + gcry_md_close(md); + return r; +} + +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { + + assert(rrsig); + assert(dnskey); + + /* Checks if the specified DNSKEY RR matches the key used for + * the signature in the specified RRSIG RR */ + + if (rrsig->key->type != DNS_TYPE_RRSIG) + return -EINVAL; + + if (dnskey->key->type != DNS_TYPE_DNSKEY) + return 0; + if (dnskey->key->class != rrsig->key->class) + return 0; + if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) + 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) + return 0; + + return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(rrsig->key)); +} + +int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) { + assert(key); + assert(rrsig); + + /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */ + + if (rrsig->key->type != DNS_TYPE_RRSIG) + return 0; + if (rrsig->key->class != key->class) + return 0; + if (rrsig->rrsig.type_covered != key->type) + return 0; + + return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); +} + +int dnssec_verify_rrset_search( + DnsAnswer *a, + DnsResourceKey *key, + DnsAnswer *validated_dnskeys, + usec_t realtime) { + + bool found_rrsig = false, found_dnskey = false; + DnsResourceRecord *rrsig; + int r; + + assert(key); + + /* Verifies all RRs from "a" that match the key "key", against DNSKEY RRs in "validated_dnskeys" */ + + if (!a || a->n_rrs <= 0) + return -ENODATA; + + /* Iterate through each RRSIG RR. */ + DNS_ANSWER_FOREACH(rrsig, a) { + DnsResourceRecord *dnskey; + + r = dnssec_key_match_rrsig(key, rrsig); + if (r < 0) + return r; + if (r == 0) + continue; + + found_rrsig = true; + + DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) { + + r = dnssec_rrsig_match_dnskey(rrsig, dnskey); + 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. */ + if (realtime == USEC_INFINITY) + realtime = now(CLOCK_REALTIME); + + /* Yay, we found a matching RRSIG with a matching + * DNSKEY, awesome. Now let's verify all entries of + * the RRSet against the RRSIG and DNSKEY + * combination. */ + + r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime); + if (r < 0 && r != EOPNOTSUPP) + 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. */ + } + } + + if (found_dnskey) + return DNSSEC_INVALID; + + if (found_rrsig) + return DNSSEC_MISSING_KEY; + + return DNSSEC_NO_SIGNATURE; +} + +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 (;;) { + size_t i; + + r = dns_label_unescape(&n, buffer, buffer_max); + if (r < 0) + return r; + if (r == 0) + break; + if (r > 0) { + int k; + + /* DNSSEC validation is always done on the ASCII version of the label */ + k = dns_label_apply_idna(buffer, r, buffer, buffer_max); + if (k < 0) + return k; + if (k > 0) + r = k; + } + + if (buffer_max < (size_t) r + 2) + return -ENOBUFS; + + /* The DNSSEC canonical form is not clear on what to + * do with dots appearing in labels, the way DNS-SD + * does it. Refuse it for now. */ + + 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'; + } + + buffer[r] = '.'; + + buffer += r + 1; + c += r + 1; + + buffer_max -= r + 1; + } + + if (c <= 0) { + /* Not even a single label: this is the root domain name */ + + assert(buffer_max > 2); + buffer[0] = '.'; + buffer[1] = 0; + + return 1; + } + + return (int) c; +} + +int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { + gcry_md_hd_t md = NULL; + char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; + void *result; + int r; + + assert(dnskey); + assert(ds); + + /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */ + + if (dnskey->key->type != DNS_TYPE_DNSKEY) + return -EINVAL; + if (ds->key->type != DNS_TYPE_DS) + return -EINVAL; + if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) + 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) + return 0; + + switch (ds->ds.digest_type) { + + case DNSSEC_DIGEST_SHA1: + + if (ds->ds.digest_size != 20) + return 0; + + gcry_md_open(&md, GCRY_MD_SHA1, 0); + break; + + case DNSSEC_DIGEST_SHA256: + + if (ds->ds.digest_size != 32) + return 0; + + gcry_md_open(&md, GCRY_MD_SHA256, 0); + break; + + default: + assert_not_reached("Unknown digest"); + } + + 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); + 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); + + result = gcry_md_read(md, 0); + if (!result) { + r = -EIO; + goto finish; + } + + r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0; + +finish: + gcry_md_close(md); + return r; +} + +static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = { + [DNSSEC_NO] = "no", + [DNSSEC_TRUST] = "trust", + [DNSSEC_YES] = "yes", +}; +DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode); diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h new file mode 100644 index 0000000000..f4cb58988a --- /dev/null +++ b/src/resolve/resolved-dns-dnssec.h @@ -0,0 +1,67 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 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/>. +***/ + +typedef enum DnssecMode DnssecMode; + +#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, + DNSSEC_INVALID, + DNSSEC_NO_SIGNATURE, + DNSSEC_MISSING_KEY, + DNSSEC_SIGNATURE_EXPIRED, +}; + +#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); + +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); + +uint16_t dnssec_keytag(DnsResourceRecord *dnskey); + +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_; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 75ca23fd08..f753b3522f 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -65,30 +65,44 @@ 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) { - 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 */, - 0 /* tc */, + 0/* tc */, 0 /* t */, 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 */, @@ -96,8 +110,25 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { 1 /* rd (ask for recursion) */, 0 /* ra */, 0 /* ad */, - 0 /* cd */, + 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; @@ -108,6 +139,8 @@ DnsPacket *dns_packet_ref(DnsPacket *p) { if (!p) return NULL; + assert(!p->on_stack); + assert(p->n_ref > 0); p->n_ref++; return p; @@ -126,7 +159,9 @@ static void dns_packet_free(DnsPacket *p) { hashmap_free(p->names); free(p->_data); - free(p); + + if (!p->on_stack) + free(p); } DnsPacket *dns_packet_unref(DnsPacket *p) { @@ -135,6 +170,9 @@ DnsPacket *dns_packet_unref(DnsPacket *p) { assert(p->n_ref > 0); + if (p->more) + dns_packet_unref(p->more); + if (p->n_ref == 1) dns_packet_free(p); else @@ -178,6 +216,13 @@ int dns_packet_validate_reply(DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + /* RFC 6762, Section 18 */ + if (DNS_PACKET_RCODE(p) != 0) + return -EBADMSG; + + break; + default: break; } @@ -219,6 +264,18 @@ int dns_packet_validate_query(DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + /* RFC 6762, Section 18 */ + if (DNS_PACKET_AA(p) != 0 || + DNS_PACKET_RD(p) != 0 || + DNS_PACKET_RA(p) != 0 || + DNS_PACKET_AD(p) != 0 || + DNS_PACKET_CD(p) != 0 || + DNS_PACKET_RCODE(p) != 0) + return -EBADMSG; + + break; + default: break; } @@ -351,25 +408,10 @@ 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) { - void *d; - size_t l; - int r; - assert(p); assert(s); - l = strlen(s); - if (l > 255) - return -E2BIG; - - r = dns_packet_extend(p, 1 + l, &d, start); - if (r < 0) - return r; - - ((uint8_t*) d)[0] = (uint8_t) l; - memcpy(((uint8_t*) d) + 1, s, l); - - return 0; + return dns_packet_append_raw_string(p, s, strlen(s), start); } int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start) { @@ -395,7 +437,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_ } int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) { - void *w; + uint8_t *w; int r; assert(p); @@ -404,12 +446,29 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start if (l > DNS_LABEL_MAX) return -E2BIG; - r = dns_packet_extend(p, 1 + l, &w, start); + r = dns_packet_extend(p, 1 + l, (void**) &w, start); if (r < 0) return r; - ((uint8_t*) w)[0] = (uint8_t) l; - memcpy(((uint8_t*) w) + 1, d, l); + *(w++) = (uint8_t) l; + + if (p->canonical_form) { + 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]; + } + } else + /* Otherwise, just copy the string unaltered. This is + * essential for DNS-SD, where the casing of labels + * matters and needs to be retained. */ + memcpy(w, d, l); return 0; } @@ -662,8 +721,8 @@ fail: return r; } -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) { - size_t saved_size, rdlength_offset, end, rdlength; +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; assert(p); @@ -684,6 +743,8 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; + rds = p->size - saved_size; + switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { case DNS_TYPE_SRV: @@ -841,11 +902,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star break; case DNS_TYPE_DNSKEY: - r = dns_packet_append_uint16(p, dnskey_to_flags(rr), NULL); + r = dns_packet_append_uint16(p, rr->dnskey.flags, NULL); if (r < 0) goto fail; - r = dns_packet_append_uint8(p, 3u, NULL); + r = dns_packet_append_uint8(p, rr->dnskey.protocol, NULL); if (r < 0) goto fail; @@ -962,6 +1023,9 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (start) *start = saved_size; + if (rdata_start) + *rdata_start = rds; + return 0; fail: @@ -1381,6 +1445,7 @@ fail: int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { _cleanup_free_ char *name = NULL; + bool cache_flush = false; uint16_t class, type; DnsResourceKey *key; size_t saved_rindex; @@ -1403,12 +1468,23 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { if (r < 0) goto fail; + if (p->protocol == DNS_PROTOCOL_MDNS) { + /* See RFC6762, Section 10.2 */ + + if (class & MDNS_RR_CACHE_FLUSH) { + class &= ~MDNS_RR_CACHE_FLUSH; + cache_flush = true; + } + } + key = dns_resource_key_new_consume(class, type, name); if (!key) { r = -ENOMEM; goto fail; } + key->cache_flush = cache_flush; + name = NULL; *ret = key; @@ -1427,17 +1503,6 @@ static bool loc_size_ok(uint8_t size) { return m <= 9 && e <= 9 && (m > 0 || e == 0); } -static int dnskey_parse_flags(DnsResourceRecord *rr, uint16_t flags) { - assert(rr); - - if (flags & ~(DNSKEY_FLAG_SEP | DNSKEY_FLAG_ZONE_KEY)) - return -EBADMSG; - - rr->dnskey.zone_key_flag = flags & DNSKEY_FLAG_ZONE_KEY; - rr->dnskey.sep_flag = flags & DNSKEY_FLAG_SEP; - return 0; -} - int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; @@ -1706,28 +1771,15 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; - case DNS_TYPE_DNSKEY: { - uint16_t flags; - uint8_t proto; - - r = dns_packet_read_uint16(p, &flags, NULL); - if (r < 0) - goto fail; - - r = dnskey_parse_flags(rr, flags); + case DNS_TYPE_DNSKEY: + r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); if (r < 0) goto fail; - r = dns_packet_read_uint8(p, &proto, NULL); + r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); if (r < 0) goto fail; - /* protocol is required to be always 3 */ - if (proto != 3) { - r = -EBADMSG; - goto fail; - } - r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); if (r < 0) goto fail; @@ -1744,7 +1796,6 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { } break; - } case DNS_TYPE_RRSIG: r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); @@ -1792,8 +1843,16 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; - case DNS_TYPE_NSEC: - r = dns_packet_read_name(p, &rr->nsec.next_domain_name, false, NULL); + case DNS_TYPE_NSEC: { + + /* + * RFC6762, section 18.14 explicly states mDNS should use name compression. + * This contradicts RFC3845, section 2.1.1 + */ + + bool allow_compressed = p->protocol == DNS_PROTOCOL_MDNS; + + r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL); if (r < 0) goto fail; @@ -1806,7 +1865,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { * without the NSEC bit set. */ break; - + } case DNS_TYPE_NSEC3: { uint8_t size; @@ -1976,17 +2035,3 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { [DNS_PROTOCOL_LLMNR] = "llmnr", }; DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); - -static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { - [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", - [DNSSEC_ALGORITHM_DH] = "DH", - [DNSSEC_ALGORITHM_DSA] = "DSA", - [DNSSEC_ALGORITHM_ECC] = "ECC", - [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", - [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1", - [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1", - [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", - [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", - [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", -}; -DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 25dfb2642f..3d84cb622b 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -88,8 +88,13 @@ struct DnsPacket { uint16_t sender_port, destination_port; uint32_t ttl; - bool extracted; - bool refuse_compression; + /* For support of truncated packets */ + DnsPacket *more; + + bool on_stack:1; + bool extracted:1; + bool refuse_compression:1; + bool canonical_form:1; }; static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { @@ -142,7 +147,9 @@ 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); +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); @@ -162,7 +169,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_ 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_key(DnsPacket *p, const DnsResourceKey *key, size_t *start); -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, 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); void dns_packet_truncate(DnsPacket *p, size_t sz); @@ -223,42 +230,25 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_; #define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) }) #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } }) -#define DNSKEY_FLAG_ZONE_KEY (1u << 8) -#define DNSKEY_FLAG_SEP (1u << 0) - -static inline uint16_t dnskey_to_flags(const DnsResourceRecord *rr) { - return (rr->dnskey.zone_key_flag * DNSKEY_FLAG_ZONE_KEY | - rr->dnskey.sep_flag * DNSKEY_FLAG_SEP); -} - -/* http://tools.ietf.org/html/rfc4034#appendix-A.1 */ -enum { - DNSSEC_ALGORITHM_RSAMD5 = 1, - DNSSEC_ALGORITHM_DH, - DNSSEC_ALGORITHM_DSA, - DNSSEC_ALGORITHM_ECC, - DNSSEC_ALGORITHM_RSASHA1, - DNSSEC_ALGORITHM_DSA_NSEC3_SHA1, - DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, - DNSSEC_ALGORITHM_INDIRECT = 252, - DNSSEC_ALGORITHM_PRIVATEDNS, - DNSSEC_ALGORITHM_PRIVATEOID, - _DNSSEC_ALGORITHM_MAX_DEFINED -}; +#define MDNS_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 251U) }) +#define MDNS_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfb } }) -const char* dnssec_algorithm_to_string(int i) _const_; -int dnssec_algorithm_from_string(const char *s) _pure_; +static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) { + uint64_t f; -static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) { + /* Converts a protocol + family into a flags field as used in queries and responses */ - /* Converts a protocol + family into a flags field as used in queries */ + f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0; switch (protocol) { case DNS_PROTOCOL_DNS: - return SD_RESOLVED_DNS; + return f|SD_RESOLVED_DNS; case DNS_PROTOCOL_LLMNR: - return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4; + return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); + + case DNS_PROTOCOL_MDNS: + return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4; default: break; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 0f3a0dd21b..089d9fb70d 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -430,15 +430,17 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { return r; /* If this a single-label domain on DNS, we might append a suitable search domain first. */ - r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question)); - if (r < 0) - goto fail; - if (r > 0) { - /* OK, we need a search domain now. Let's find one for this scope */ - - r = dns_query_candidate_next_search_domain(c); - if (r <= 0) /* if there's no search domain, then we won't add any transaction. */ + if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) { + r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question)); + if (r < 0) goto fail; + if (r > 0) { + /* OK, we need a search domain now. Let's find one for this scope */ + + r = dns_query_candidate_next_search_domain(c); + if (r <= 0) /* if there's no search domain, then we won't add any transaction. */ + goto fail; + } } r = dns_query_candidate_setup_transactions(c); @@ -970,6 +972,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; DnsTransaction *t; Iterator i; + bool has_authenticated = false, has_non_authenticated = false; assert(q); @@ -997,6 +1000,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { q->answer = merged; q->answer_rcode = t->answer_rcode; + if (t->answer_authenticated) + has_authenticated = true; + else + has_non_authenticated = true; + state = DNS_TRANSACTION_SUCCESS; break; } @@ -1026,6 +1034,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { 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); diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index a9d7904a8d..b71bb2352b 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -75,6 +75,7 @@ struct DnsQuery { DnsProtocol answer_protocol; int answer_family; DnsSearchDomain *answer_search_domain; + bool answer_authenticated; /* Bus client information */ sd_bus_message *request; diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index e77116c03a..5ffb63e250 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -23,9 +23,10 @@ typedef struct DnsQuestion DnsQuestion; +#include "macro.h" #include "resolved-dns-rr.h" -/* A simple array of resources keys */ +/* A simple array of resource keys */ struct DnsQuestion { unsigned n_ref; @@ -55,10 +56,12 @@ const char *dns_question_first_name(DnsQuestion *q); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); -#define DNS_QUESTION_FOREACH(key, q) \ - for (unsigned _i = ({ \ +#define _DNS_QUESTION_FOREACH(u, key, q) \ + for (unsigned UNIQ_T(i, u) = ({ \ (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \ 0; \ }); \ - (q) && ((_i) < (q)->n_keys); \ - _i++, (key) = (_i < (q)->n_keys ? (q)->keys[_i] : NULL)) + (q) && (UNIQ_T(i, u) < (q)->n_keys); \ + UNIQ_T(i, u)++, (key) = (UNIQ_T(i, u) < (q)->n_keys ? (q)->keys[UNIQ_T(i, u)] : NULL)) + +#define DNS_QUESTION_FOREACH(key, q) _DNS_QUESTION_FOREACH(UNIQ, key, q) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 4a1abb0cdc..934a18334c 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -27,6 +27,7 @@ #include "hexdecoct.h" #include "resolved-dns-packet.h" #include "resolved-dns-rr.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" @@ -50,12 +51,6 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char * return k; } -DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) { - assert(key); - - return dns_resource_key_new(key->class, DNS_TYPE_CNAME, DNS_RESOURCE_KEY_NAME(key)); -} - DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) { int r; @@ -136,6 +131,10 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) { if (!k) return NULL; + /* Static/const keys created with DNS_RESOURCE_KEY_CONST will + * set this to -1, they should not be reffed/unreffed */ + assert(k->n_ref != (unsigned) -1); + assert(k->n_ref > 0); k->n_ref++; @@ -146,6 +145,7 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) { if (!k) return NULL; + assert(k->n_ref != (unsigned) -1); assert(k->n_ref > 0); if (k->n_ref == 1) { @@ -157,6 +157,14 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) { return NULL; } +bool dns_resource_key_is_address(const DnsResourceKey *key) { + assert(key); + + /* Check if this is an A or AAAA resource key */ + + return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA); +} + int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { int r; @@ -416,6 +424,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { free(rr->generic.data); } + free(rr->wire_format); dns_resource_key_unref(rr->key); } @@ -576,8 +585,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0; case DNS_TYPE_DNSKEY: - return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag && - a->dnskey.sep_flag == b->dnskey.sep_flag && + return a->dnskey.flags == b->dnskey.flags && + a->dnskey.protocol == b->dnskey.protocol && a->dnskey.algorithm == b->dnskey.algorithm && a->dnskey.key_size == b->dnskey.key_size && memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0; @@ -883,9 +892,10 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { if (!t) return -ENOMEM; - r = asprintf(&s, "%s %u 3 %.*s%.*u %s", + r = asprintf(&s, "%s %u %u %.*s%.*u %s", k, - dnskey_to_flags(rr), + rr->dnskey.flags, + rr->dnskey.protocol, alg ? -1 : 0, alg, alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm, t); @@ -993,6 +1003,51 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { return 0; } +int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) { + + DnsPacket packet = { + .n_ref = 1, + .protocol = DNS_PROTOCOL_DNS, + .on_stack = true, + .refuse_compression = true, + .canonical_form = canonical, + }; + + size_t start, rds; + int r; + + assert(rr); + + /* Generates the RR in wire-format, optionally in the + * canonical form as discussed in the DNSSEC RFC 4034, Section + * 6.2. We allocate a throw-away DnsPacket object on the stack + * here, because we need some book-keeping for memory + * management, and can reuse the DnsPacket serializer, that + * can generate the canonical form, too, but also knows label + * compression and suchlike. */ + + if (rr->wire_format && rr->wire_format_canonical == canonical) + return 0; + + r = dns_packet_append_rr(&packet, rr, &start, &rds); + if (r < 0) + return r; + + assert(start == 0); + assert(packet._data); + + free(rr->wire_format); + rr->wire_format = packet._data; + rr->wire_format_size = packet.size; + rr->wire_format_rdata_offset = rds; + rr->wire_format_canonical = canonical; + + packet._data = NULL; + dns_packet_unref(&packet); + + return 0; +} + const char *dns_class_to_string(uint16_t class) { switch (class) { @@ -1049,3 +1104,25 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) { return dns_txt_item_equal(a->items_next, b->items_next); } + +static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { + [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", + [DNSSEC_ALGORITHM_DH] = "DH", + [DNSSEC_ALGORITHM_DSA] = "DSA", + [DNSSEC_ALGORITHM_ECC] = "ECC", + [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", + [DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1", + [DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1", + [DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256", + [DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512", + [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", + [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", + [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", +}; +DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); + +static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = { + [DNSSEC_DIGEST_SHA1] = "SHA1", + [DNSSEC_DIGEST_SHA256] = "SHA256", +}; +DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int); diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index a092961823..5c2306ba96 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -41,12 +41,60 @@ enum { _DNS_CLASS_INVALID = -1 }; +/* DNSKEY RR flags */ +#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) +#define DNSKEY_FLAG_SEP (UINT16_C(1) << 0) + +/* mDNS RR flags */ +#define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15) + +/* DNSSEC algorithm identifiers, see + * http://tools.ietf.org/html/rfc4034#appendix-A.1 and + * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */ +enum { + DNSSEC_ALGORITHM_RSAMD5 = 1, + DNSSEC_ALGORITHM_DH, + DNSSEC_ALGORITHM_DSA, + DNSSEC_ALGORITHM_ECC, + 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_INDIRECT = 252, + DNSSEC_ALGORITHM_PRIVATEDNS, + DNSSEC_ALGORITHM_PRIVATEOID, + _DNSSEC_ALGORITHM_MAX_DEFINED +}; + +/* DNSSEC digest identifiers, see + * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ +enum { + DNSSEC_DIGEST_SHA1 = 1, + DNSSEC_DIGEST_SHA256 = 2, + _DNSSEC_DIGEST_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 + * look up something, without allocating a full DnsResourceKey object + * for it. Note that it is not OK to take references to this kind of + * resource key object. */ +#define DNS_RESOURCE_KEY_CONST(c, t, n) \ + ((DnsResourceKey) { \ + .n_ref = (unsigned) -1, \ + .class = c, \ + .type = t, \ + ._name = (char*) n, \ + }) + + struct DnsTxtItem { size_t length; LIST_FIELDS(DnsTxtItem, items); @@ -57,7 +105,11 @@ struct DnsResourceRecord { unsigned n_ref; DnsResourceKey *key; uint32_t ttl; - bool unparseable; + bool unparseable:1; + bool wire_format_canonical:1; + void *wire_format; + size_t wire_format_size; + size_t wire_format_rdata_offset; union { struct { void *data; @@ -135,8 +187,8 @@ struct DnsResourceRecord { /* http://tools.ietf.org/html/rfc4034#section-2.1 */ struct { - bool zone_key_flag:1; - bool sep_flag:1; + uint16_t flags; + uint8_t protocol; uint8_t algorithm; void* key; size_t key_size; @@ -186,19 +238,22 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { } DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); -DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key); -DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key); 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); DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); 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_to_string(const DnsResourceKey *key, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); +static inline bool dns_key_is_shared(const DnsResourceKey *key) { + return IN_SET(key->type, DNS_TYPE_PTR); +} + DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key); DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name); DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); @@ -209,6 +264,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); +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); @@ -216,3 +273,9 @@ 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; + +const char* dnssec_algorithm_to_string(int i) _const_; +int dnssec_algorithm_from_string(const char *s) _pure_; + +const char *dnssec_digest_to_string(int i) _const_; +int dnssec_digest_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 09e6872d26..4d83ac597c 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -30,6 +30,7 @@ #include "random-util.h" #include "resolved-dns-scope.h" #include "resolved-llmnr.h" +#include "resolved-mdns.h" #include "socket-util.h" #include "strv.h" @@ -59,6 +60,7 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int LIST_PREPEND(scopes, m->dns_scopes, s); dns_scope_llmnr_membership(s, true); + dns_scope_mdns_membership(s, true); log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family)); @@ -95,6 +97,7 @@ DnsScope* dns_scope_free(DnsScope *s) { log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); dns_scope_llmnr_membership(s, false); + dns_scope_mdns_membership(s, false); dns_scope_abort_transactions(s); while (s->query_candidates) @@ -158,11 +161,10 @@ 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, DnsServer *server, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; - uint16_t port; uint32_t mtu; size_t saved_size = 0; @@ -228,7 +230,6 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { return -EBUSY; family = s->family; - port = LLMNR_PORT; if (family == AF_INET) { addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; @@ -241,7 +242,30 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { if (fd < 0) return fd; - r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); + r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p); + if (r < 0) + return r; + + break; + + case DNS_PROTOCOL_MDNS: + if (!ratelimit_test(&s->ratelimit)) + return -EBUSY; + + family = s->family; + + if (family == AF_INET) { + addr.in = MDNS_MULTICAST_IPV4_ADDRESS; + fd = manager_mdns_ipv4_fd(s->manager); + } else if (family == AF_INET6) { + addr.in6 = MDNS_MULTICAST_IPV6_ADDRESS; + fd = manager_mdns_ipv6_fd(s->manager); + } else + return -EAFNOSUPPORT; + if (fd < 0) + return fd; + + r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p); if (r < 0) return r; @@ -254,6 +278,31 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { return 1; } +int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, 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, server, 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, uint16_t port, DnsServer **server) { DnsServer *srv = NULL; _cleanup_close_ int fd = -1; @@ -368,13 +417,14 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co assert(s); assert(domain); - if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) - return DNS_SCOPE_NO; + /* Checks if the specified domain is something to look up on + * this scope. Note that this accepts non-qualified hostnames, + * i.e. those without any search path prefixed yet. */ - if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0) + if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) return DNS_SCOPE_NO; - if (dns_name_is_root(domain)) + if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0) return DNS_SCOPE_NO; /* Never resolve any loopback hostname or IP address via DNS, @@ -385,6 +435,12 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; + /* Never respond to some of the domains listed in RFC6303 */ + if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 || + dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 || + 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; + /* 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 @@ -397,10 +453,16 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co case DNS_PROTOCOL_DNS: - if ((!dns_name_is_single_label(domain) || - (!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) && - dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0) + /* Exclude link-local IP ranges */ + if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && + 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 && + /* 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; @@ -434,8 +496,27 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { assert(s); assert(key); - if (s->protocol == DNS_PROTOCOL_DNS) - return true; + /* Check if it makes sense to resolve the specified key on + * this scope. Note that this call assumes as fully qualified + * name, i.e. the search suffixes already appended. */ + + if (s->protocol == DNS_PROTOCOL_DNS) { + + /* On classic DNS, lookin 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.) */ + if (!dns_resource_key_is_address(key)) + return true; + + /* However, we refuse to look up A and AAAA RRs on the + * root and single-label domains, under the assumption + * that those should be resolved via LLMNR or search + * path only, and should not be leaked onto the + * internet. */ + return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) || + dns_name_is_root(DNS_RESOURCE_KEY_NAME(key))); + } /* On mDNS and LLMNR, send A and AAAA queries only on the * respective scopes */ @@ -449,19 +530,15 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { return true; } -int dns_scope_llmnr_membership(DnsScope *s, bool b) { +static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) { int fd; assert(s); - - if (s->protocol != DNS_PROTOCOL_LLMNR) - return 0; - assert(s->link); if (s->family == AF_INET) { struct ip_mreqn mreqn = { - .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS, + .imr_multiaddr = in, .imr_ifindex = s->link->ifindex, }; @@ -480,7 +557,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { } else if (s->family == AF_INET6) { struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS, + .ipv6mr_multiaddr = in6, .ipv6mr_interface = s->link->ifindex, }; @@ -499,6 +576,22 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { return 0; } +int dns_scope_llmnr_membership(DnsScope *s, bool b) { + + if (s->protocol != DNS_PROTOCOL_LLMNR) + return 0; + + return dns_scope_multicast_membership(s, b, LLMNR_MULTICAST_IPV4_ADDRESS, LLMNR_MULTICAST_IPV6_ADDRESS); +} + +int dns_scope_mdns_membership(DnsScope *s, bool b) { + + if (s->protocol != DNS_PROTOCOL_MDNS) + return 0; + + return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS); +} + static int dns_scope_make_reply_packet( DnsScope *s, uint16_t id, @@ -549,7 +642,7 @@ static int dns_scope_make_reply_packet( if (answer) { for (i = 0; i < answer->n_rrs; i++) { - r = dns_packet_append_rr(p, answer->items[i].rr, NULL); + r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL); if (r < 0) return r; } @@ -559,7 +652,7 @@ static int dns_scope_make_reply_packet( if (soa) { for (i = 0; i < soa->n_rrs; i++) { - r = dns_packet_append_rr(p, soa->items[i].rr, NULL); + r = dns_packet_append_rr(p, soa->items[i].rr, NULL, NULL); if (r < 0) return r; } @@ -733,7 +826,7 @@ static int dns_scope_make_conflict_packet( if (r < 0) return r; - r = dns_packet_append_rr(p, rr, NULL); + r = dns_packet_append_rr(p, rr, NULL, NULL); if (r < 0) return r; @@ -901,34 +994,13 @@ void dns_scope_dump(DnsScope *s, FILE *f) { DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { assert(s); - /* Returns the list of *local* search domains -- not the - * global ones. */ - if (s->protocol != DNS_PROTOCOL_DNS) return NULL; if (s->link) return s->link->search_domains; - return NULL; -} - -bool dns_scope_has_search_domains(DnsScope *s) { - assert(s); - - /* Tests if there are *any* search domains suitable for this - * scope. This means either local or global ones */ - - if (s->protocol != DNS_PROTOCOL_DNS) - return false; - - if (s->manager->search_domains) - return true; - - if (s->link && s->link->search_domains) - return true; - - return false; + return s->manager->search_domains; } bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 0480f702f8..2fc2e07deb 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -26,6 +26,7 @@ typedef struct DnsScope DnsScope; #include "resolved-dns-cache.h" +#include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" #include "resolved-dns-server.h" #include "resolved-dns-zone.h" @@ -44,6 +45,7 @@ struct DnsScope { DnsProtocol protocol; int family; + DnssecMode dnssec_mode; Link *link; @@ -91,6 +93,7 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s); void dns_scope_next_dns_server(DnsScope *s); int dns_scope_llmnr_membership(DnsScope *s, bool b); +int dns_scope_mdns_membership(DnsScope *s, bool b); void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); @@ -102,6 +105,5 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); void dns_scope_dump(DnsScope *s, FILE *f); DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s); -bool dns_scope_has_search_domains(DnsScope *s); bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 00366a48c9..b07fc3af3d 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -61,10 +61,11 @@ struct DnsServer { int family; union in_addr_union address; + bool marked:1; + usec_t resend_timeout; usec_t max_rtt; - bool marked:1; DnsServerFeatureLevel verified_features; DnsServerFeatureLevel possible_features; size_t received_udp_packet_max; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 90133cb332..90f07e6c4b 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -24,6 +24,7 @@ #include "dns-domain.h" #include "fd-util.h" #include "random-util.h" +#include "resolved-dns-cache.h" #include "resolved-dns-transaction.h" #include "resolved-llmnr.h" #include "string-table.h" @@ -243,7 +244,7 @@ static int on_stream_complete(DnsStream *s, int error) { } if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid LLMNR TCP packet."); + log_debug("Invalid TCP reply packet."); dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); return 0; } @@ -384,6 +385,18 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { break; + case DNS_PROTOCOL_MDNS: + assert(t->scope->link); + + /* For mDNS we will not accept any packets from other interfaces */ + if (p->ifindex != t->scope->link->ifindex) + return; + + if (p->family != t->scope->family) + return; + + break; + case DNS_PROTOCOL_DNS: break; @@ -446,6 +459,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } if (DNS_PACKET_TC(p)) { + + /* Truncated packets for mDNS are not allowed. Give up immediately. */ + if (t->scope->protocol == DNS_PROTOCOL_MDNS) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } + /* Response was truncated, let's try again with good old TCP */ r = dns_transaction_open_tcp(t); if (r == -ESRCH) { @@ -454,7 +474,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } if (r < 0) { - /* On LLMNR, if we cannot connect to the host, + /* On LLMNR and mDNS, if we cannot connect to the host, * we immediately give up */ if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); @@ -481,21 +501,32 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } - /* Install the answer as answer to the transaction */ - dns_answer_unref(t->answer); - t->answer = dns_answer_ref(p->answer); - t->answer_rcode = DNS_PACKET_RCODE(p); + if (t->scope->protocol == DNS_PROTOCOL_DNS) { + /* 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) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } - /* 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) { - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; + /* Install the answer as answer to the transaction */ + 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); } - /* 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), 0, p->family, &p->sender); - if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); else @@ -562,21 +593,26 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat assert(s); assert(t); - /* Timeout reached? Increase the timeout for the server used */ - switch (t->scope->protocol) { - case DNS_PROTOCOL_DNS: - assert(t->server); + 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->current_features, usec - t->start_usec); - break; - case DNS_PROTOCOL_LLMNR: - case DNS_PROTOCOL_MDNS: - dns_scope_packet_lost(t->scope, 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."); + break; + default: + assert_not_reached("Invalid DNS protocol."); + } + + if (t->initial_jitter_scheduled) + t->initial_jitter_elapsed = true; } /* ...and try again with a new server */ @@ -589,38 +625,6 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat return 0; } -static int dns_transaction_make_packet(DnsTransaction *t) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - - assert(t); - - if (t->sent) - return 0; - - r = dns_packet_new_query(&p, t->scope->protocol, 0); - if (r < 0) - return r; - - r = dns_scope_good_key(t->scope, t->key); - if (r < 0) - return r; - if (r == 0) - return -EDOM; - - r = dns_packet_append_key(p, t->key, NULL); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->qdcount = htobe16(1); - DNS_PACKET_HEADER(p)->id = t->id; - - t->sent = p; - p = NULL; - - return 0; -} - static usec_t transaction_get_resend_timeout(DnsTransaction *t) { assert(t); assert(t->scope); @@ -630,17 +634,18 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) { assert(t->server); return t->server->resend_timeout; - case DNS_PROTOCOL_LLMNR: 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."); } } -int dns_transaction_go(DnsTransaction *t) { +static int dns_transaction_prepare_next_attempt(DnsTransaction *t, usec_t ts) { bool had_stream; - usec_t ts; int r; assert(t); @@ -649,11 +654,6 @@ int dns_transaction_go(DnsTransaction *t) { dns_transaction_stop(t); - log_debug("Excercising transaction on scope %s on %s/%s", - 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)); - if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) { dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED); return 0; @@ -666,8 +666,6 @@ int dns_transaction_go(DnsTransaction *t) { return 0; } - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); - t->n_attempts++; t->start_usec = ts; t->received = dns_packet_unref(t->received); @@ -675,7 +673,21 @@ int dns_transaction_go(DnsTransaction *t) { t->answer_rcode = 0; t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID; - /* Check the zone, but obly if this transaction is not used + /* 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); + if (r < 0) + return r; + if (r > 0) { + t->answer_rcode = DNS_RCODE_SUCCESS; + t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR; + t->answer_authenticated = true; + dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); + 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)) { @@ -685,6 +697,7 @@ int dns_transaction_go(DnsTransaction *t) { if (r > 0) { t->answer_rcode = DNS_RCODE_SUCCESS; t->answer_source = DNS_TRANSACTION_ZONE; + t->answer_authenticated = true; dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); return 0; } @@ -702,7 +715,7 @@ int dns_transaction_go(DnsTransaction *t) { /* Let's then prune all outdated entries */ dns_cache_prune(&t->scope->cache); - r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer); + r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated); if (r < 0) return r; if (r > 0) { @@ -715,31 +728,203 @@ int dns_transaction_go(DnsTransaction *t) { } } - if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) { - usec_t jitter; + return 1; +} + +static int dns_transaction_make_packet_mdns(DnsTransaction *t) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + bool add_known_answers = false; + DnsTransaction *other; + unsigned qdcount; + usec_t ts; + int r; + + assert(t); + assert(t->scope->protocol == DNS_PROTOCOL_MDNS); + + /* Discard any previously prepared packet, so we can start over and coaleasce again */ + t->sent = dns_packet_unref(t->sent); + + r = dns_packet_new_query(&p, t->scope->protocol, 0, false); + if (r < 0) + return r; + + r = dns_packet_append_key(p, t->key, NULL); + if (r < 0) + return r; + + qdcount = 1; + + if (dns_key_is_shared(t->key)) + add_known_answers = true; + + /* + * For mDNS, we want to coalesce as many open queries in pending transactions into one single + * query packet on the wire as possible. To achieve that, we iterate through all pending transactions + * in our current scope, and see whether their timing contraints allow them to be sent. + */ + + assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + + LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) { + + /* Skip ourselves */ + if (other == t) + continue; + + if (other->state != DNS_TRANSACTION_PENDING) + continue; + + if (other->next_attempt_after > ts) + continue; + + if (qdcount >= UINT16_MAX) + break; + + r = dns_packet_append_key(p, other->key, NULL); + + /* + * If we can't stuff more questions into the packet, just give up. + * One of the 'other' transactions will fire later and take care of the rest. + */ + if (r == -EMSGSIZE) + break; + + if (r < 0) + return r; + + r = dns_transaction_prepare_next_attempt(other, ts); + if (r <= 0) + continue; + + ts += transaction_get_resend_timeout(other); + + r = sd_event_add_time( + other->scope->manager->event, + &other->timeout_event_source, + clock_boottime_or_monotonic(), + ts, 0, + on_transaction_timeout, other); + if (r < 0) + return r; + + other->state = DNS_TRANSACTION_PENDING; + other->next_attempt_after = ts; + + qdcount ++; + + if (dns_key_is_shared(other->key)) + add_known_answers = true; + } + + 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) { + r = dns_cache_export_shared_to_packet(&t->scope->cache, p); + if (r < 0) + return r; + } + + t->sent = p; + p = NULL; + + return 0; +} + +static int dns_transaction_make_packet(DnsTransaction *t) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + assert(t); + + if (t->scope->protocol == DNS_PROTOCOL_MDNS) + return dns_transaction_make_packet_mdns(t); + + if (t->sent) + return 0; + + r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES); + if (r < 0) + return r; + + r = dns_scope_good_key(t->scope, t->key); + if (r < 0) + return r; + if (r == 0) + return -EDOM; + + r = dns_packet_append_key(p, t->key, NULL); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->qdcount = htobe16(1); + DNS_PACKET_HEADER(p)->id = t->id; + + t->sent = p; + p = NULL; + + return 0; +} + +int dns_transaction_go(DnsTransaction *t) { + usec_t ts; + int r; + + 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); + if (r <= 0) + return r; + + log_debug("Excercising transaction on scope %s on %s/%s", + 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)); + + if (!t->initial_jitter_scheduled && + (t->scope->protocol == DNS_PROTOCOL_LLMNR || + t->scope->protocol == DNS_PROTOCOL_MDNS)) { + usec_t jitter, accuracy; /* RFC 4795 Section 2.7 suggests all queries should be * delayed by a random time from 0 to JITTER_INTERVAL. */ - t->initial_jitter = true; + t->initial_jitter_scheduled = true; random_bytes(&jitter, sizeof(jitter)); - jitter %= LLMNR_JITTER_INTERVAL_USEC; + + 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; + accuracy = MDNS_JITTER_RANGE_USEC; + break; + default: + assert_not_reached("bad protocol"); + } r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), - ts + jitter, - LLMNR_JITTER_INTERVAL_USEC, + ts + jitter, accuracy, on_transaction_timeout, t); if (r < 0) return r; t->n_attempts = 0; + t->next_attempt_after = ts; t->state = DNS_TRANSACTION_PENDING; - log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter); + log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter); return 0; } @@ -786,16 +971,20 @@ int dns_transaction_go(DnsTransaction *t) { return dns_transaction_go(t); } + ts += transaction_get_resend_timeout(t); + r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), - ts + transaction_get_resend_timeout(t), 0, + ts, 0, on_transaction_timeout, t); if (r < 0) return r; t->state = DNS_TRANSACTION_PENDING; + t->next_attempt_after = ts; + return 1; } @@ -817,5 +1006,6 @@ static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MA [DNS_TRANSACTION_NETWORK] = "network", [DNS_TRANSACTION_CACHE] = "cache", [DNS_TRANSACTION_ZONE] = "zone", + [DNS_TRANSACTION_TRUST_ANCHOR] = "trust-anchor", }; DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 5778913cc8..af08b20e44 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -44,6 +44,7 @@ enum DnsTransactionSource { DNS_TRANSACTION_NETWORK, DNS_TRANSACTION_CACHE, DNS_TRANSACTION_ZONE, + DNS_TRANSACTION_TRUST_ANCHOR, _DNS_TRANSACTION_SOURCE_MAX, _DNS_TRANSACTION_SOURCE_INVALID = -1 }; @@ -61,15 +62,18 @@ struct DnsTransaction { DnsTransactionState state; uint16_t id; - bool initial_jitter; + bool initial_jitter_scheduled; + bool initial_jitter_elapsed; DnsPacket *sent, *received; DnsAnswer *answer; int answer_rcode; DnsTransactionSource answer_source; + bool answer_authenticated; usec_t start_usec; + usec_t next_attempt_after; sd_event_source *timeout_event_source; unsigned n_attempts; @@ -117,6 +121,10 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_; /* LLMNR Jitter interval, see RFC 4795 Section 7 */ #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC) +/* mDNS Jitter interval, see RFC 6762 Section 5.2 */ +#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC) +#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC) + /* Maximum attempts to send DNS requests, across all DNS servers */ #define DNS_TRANSACTION_ATTEMPTS_MAX 16 diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c new file mode 100644 index 0000000000..e55bdaa1ed --- /dev/null +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -0,0 +1,101 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "resolved-dns-trust-anchor.h" + +/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml */ +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) { + _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); + if (r < 0) + return r; + + if (hashmap_get(d->by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, "."))) + return 0; + + /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */ + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, ""); + if (!rr) + return -ENOMEM; + + rr->ds.key_tag = 19036; + rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rr->ds.digest_type = DNSSEC_DIGEST_SHA256; + rr->ds.digest_size = sizeof(root_digest); + rr->ds.digest = memdup(root_digest, rr->ds.digest_size); + if (!rr->ds.digest) + return -ENOMEM; + + answer = dns_answer_new(1); + if (!answer) + return -ENOMEM; + + r = dns_answer_add(answer, rr, 0); + if (r < 0) + return r; + + r = hashmap_put(d->by_key, rr->key, answer); + if (r < 0) + return r; + + answer = NULL; + return 0; +} + +void dns_trust_anchor_flush(DnsTrustAnchor *d) { + DnsAnswer *a; + + assert(d); + + while ((a = hashmap_steal_first(d->by_key))) + dns_answer_unref(a); + + d->by_key = hashmap_free(d->by_key); +} + +int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer **ret) { + DnsAnswer *a; + + assert(d); + assert(key); + assert(ret); + + /* We only serve DS and DNSKEY RRs. */ + if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY)) + return 0; + + a = hashmap_get(d->by_key, key); + if (!a) + return 0; + + *ret = dns_answer_ref(a); + return 1; +} diff --git a/src/resolve/resolved-dns-trust-anchor.h b/src/resolve/resolved-dns-trust-anchor.h new file mode 100644 index 0000000000..06f3723914 --- /dev/null +++ b/src/resolve/resolved-dns-trust-anchor.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 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/>. +***/ + +typedef struct DnsTrustAnchor DnsTrustAnchor; + +#include "hashmap.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-rr.h" + +/* This contains a fixed database mapping domain names to DS or DNSKEY records. */ + +struct DnsTrustAnchor { + Hashmap *by_key; +}; + +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); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 493d11dd14..78f44d51a2 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -163,7 +163,6 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { } static int dns_zone_item_probe_start(DnsZoneItem *i) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; DnsTransaction *t; int r; @@ -172,12 +171,14 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (i->probe_transaction) return 0; - key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)); - if (!key) - return -ENOMEM; - - t = dns_scope_find_transaction(i->scope, key, false); + t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)), false); if (!t) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)); + if (!key) + return -ENOMEM; + r = dns_transaction_new(&t, i->scope, key); if (r < 0) return r; diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 50662656d5..c815eae850 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -18,3 +18,4 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 Resolve.Domains, config_parse_search_domains, 0, 0 Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) +Resolve.DNSSEC, config_parse_dnssec, 0, 0 diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index ddd9427dab..84100bd988 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -77,6 +77,8 @@ Link *link_free(Link *l) { dns_scope_free(l->unicast_scope); dns_scope_free(l->llmnr_ipv4_scope); dns_scope_free(l->llmnr_ipv6_scope); + dns_scope_free(l->mdns_ipv4_scope); + dns_scope_free(l->mdns_ipv6_scope); free(l); return NULL; @@ -118,6 +120,28 @@ static void link_allocate_scopes(Link *l) { } } else l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); + + if (link_relevant(l, AF_INET) && + l->mdns_support != SUPPORT_NO && + l->manager->mdns_support != SUPPORT_NO) { + if (!l->mdns_ipv4_scope) { + r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET); + if (r < 0) + log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m"); + } + } else + l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope); + + if (link_relevant(l, AF_INET6) && + l->mdns_support != SUPPORT_NO && + l->manager->mdns_support != SUPPORT_NO) { + if (!l->mdns_ipv6_scope) { + r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6); + if (r < 0) + log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m"); + } + } else + l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope); } void link_add_rrs(Link *l, bool force_remove) { diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index eb00015bd5..a3b406bbc2 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -67,10 +67,13 @@ struct Link { unsigned n_search_domains; Support llmnr_support; + Support mdns_support; DnsScope *unicast_scope; DnsScope *llmnr_ipv4_scope; DnsScope *llmnr_ipv6_scope; + DnsScope *mdns_ipv4_scope; + DnsScope *mdns_ipv6_scope; char name[IF_NAMESIZE]; uint32_t mtu; diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 6a7ff9d245..ed754c3899 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -461,10 +461,8 @@ 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; - } return m->llmnr_ipv6_tcp_fd; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 62562f0d24..a2677f442a 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -40,6 +40,7 @@ #include "resolved-llmnr.h" #include "resolved-manager.h" #include "resolved-resolv-conf.h" +#include "resolved-mdns.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -472,12 +473,17 @@ int manager_new(Manager **ret) { m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1; m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1; + m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1; m->hostname_fd = -1; m->llmnr_support = SUPPORT_YES; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; + r = dns_trust_anchor_load(&m->trust_anchor); + if (r < 0) + return r; + r = sd_event_default(&m->event); if (r < 0) return r; @@ -524,6 +530,10 @@ int manager_start(Manager *m) { if (r < 0) return r; + r = manager_mdns_start(m); + if (r < 0) + return r; + return 0; } @@ -555,6 +565,7 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->rtnl_event_source); manager_llmnr_stop(m); + manager_mdns_stop(m); sd_bus_slot_unref(m->prepare_for_sleep_slot); sd_event_source_unref(m->bus_retry_event_source); @@ -572,6 +583,8 @@ Manager *manager_free(Manager *m) { free(m->llmnr_hostname); free(m->mdns_hostname); + dns_trust_anchor_flush(&m->trust_anchor); + free(m); return NULL; @@ -1018,11 +1031,25 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { if (!l) return NULL; - if (p->protocol == DNS_PROTOCOL_LLMNR) { + switch (p->protocol) { + case DNS_PROTOCOL_LLMNR: if (p->family == AF_INET) return l->llmnr_ipv4_scope; else if (p->family == AF_INET6) return l->llmnr_ipv6_scope; + + break; + + case DNS_PROTOCOL_MDNS: + if (p->family == AF_INET) + return l->mdns_ipv4_scope; + else if (p->family == AF_INET6) + return l->mdns_ipv6_scope; + + break; + + default: + break; } return NULL; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index d00c444583..b52273403a 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -44,6 +44,7 @@ enum Support { #include "resolved-dns-search-domain.h" #include "resolved-dns-server.h" #include "resolved-dns-stream.h" +#include "resolved-dns-trust-anchor.h" #include "resolved-link.h" #define MANAGER_SEARCH_DOMAINS_MAX 32 @@ -53,6 +54,7 @@ struct Manager { sd_event *event; Support llmnr_support; + Support mdns_support; /* Network */ Hashmap *links; @@ -85,6 +87,8 @@ struct Manager { bool read_resolv_conf:1; usec_t resolv_conf_mtime; + DnsTrustAnchor trust_anchor; + LIST_HEAD(DnsScope, dns_scopes); DnsScope *unicast_scope; @@ -99,6 +103,13 @@ struct Manager { sd_event_source *llmnr_ipv4_tcp_event_source; sd_event_source *llmnr_ipv6_tcp_event_source; + /* mDNS */ + int mdns_ipv4_fd; + int mdns_ipv6_fd; + + sd_event_source *mdns_ipv4_event_source; + sd_event_source *mdns_ipv6_event_source; + /* dbus */ sd_bus *bus; sd_event_source *bus_retry_event_source; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c new file mode 100644 index 0000000000..abe63d58c1 --- /dev/null +++ b/src/resolve/resolved-mdns.c @@ -0,0 +1,286 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + 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 <resolv.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "fd-util.h" +#include "resolved-manager.h" +#include "resolved-mdns.h" + +void manager_mdns_stop(Manager *m) { + assert(m); + + m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source); + m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd); + + m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source); + m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd); +} + +int manager_mdns_start(Manager *m) { + int r; + + assert(m); + + if (m->mdns_support == SUPPORT_NO) + return 0; + + r = manager_mdns_ipv4_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + if (socket_ipv6_is_supported()) { + r = manager_mdns_ipv6_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + } + + return 0; + +eaddrinuse: + log_warning("There appears to be another mDNS responder running. Turning off mDNS support."); + m->mdns_support = SUPPORT_NO; + manager_mdns_stop(m); + + return 0; +} + +static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + Manager *m = userdata; + DnsScope *scope; + int r; + + r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p); + if (r <= 0) + return r; + + scope = manager_find_scope(m, p); + if (!scope) { + log_warning("Got mDNS UDP packet on unknown scope. Ignoring."); + return 0; + } + + if (dns_packet_validate_reply(p) > 0) { + unsigned i; + + log_debug("Got mDNS reply packet"); + + /* + * mDNS is different from regular DNS and LLMNR with regard to handling responses. + * While on other protocols, we can ignore every answer that doesn't match a question + * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all + * incoming information, regardless of the DNS packet ID. + * + * Hence, extract the packet here, and try to find a transaction for answer the we got + * and complete it. Also store the new information in scope's cache. + */ + r = dns_packet_extract(p); + if (r < 0) { + log_debug("mDNS packet extraction failed."); + return 0; + } + + dns_scope_check_conflicts(scope, p); + + for (i = 0; i < p->answer->n_rrs; i++) { + DnsResourceRecord *rr; + DnsTransaction *t; + + rr = p->answer->items[i].rr; + + 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); + + } else if (dns_packet_validate_query(p) > 0) { + log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p)); + + dns_scope_process_query(scope, NULL, p); + } else + log_debug("Invalid mDNS UDP packet."); + + return 0; +} + +int manager_mdns_ipv4_fd(Manager *m) { + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(MDNS_PORT), + }; + static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255; + int r; + + assert(m); + + if (m->mdns_ipv4_fd >= 0) + return m->mdns_ipv4_fd; + + m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->mdns_ipv4_fd < 0) + return -errno; + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* Disable Don't-Fragment bit in the IP header */ + r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m); + if (r < 0) + goto fail; + + return m->mdns_ipv4_fd; + +fail: + m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd); + return r; +} + +int manager_mdns_ipv6_fd(Manager *m) { + union sockaddr_union sa = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(MDNS_PORT), + }; + static const int one = 1, ttl = 255; + int r; + + assert(m); + + if (m->mdns_ipv6_fd >= 0) + return m->mdns_ipv6_fd; + + m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->mdns_ipv6_fd < 0) + return -errno; + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m); + if (r < 0) + goto fail; + + return m->mdns_ipv6_fd; + +fail: + m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd); + return r; +} diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h new file mode 100644 index 0000000000..8a84010615 --- /dev/null +++ b/src/resolve/resolved-mdns.h @@ -0,0 +1,32 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "resolved-manager.h" + +#define MDNS_PORT 5353 + +int manager_mdns_ipv4_fd(Manager *m); +int manager_mdns_ipv6_fd(Manager *m); + +void manager_mdns_stop(Manager *m); +int manager_mdns_start(Manager *m); diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 39ecf83217..efc9c6733a 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,3 +16,4 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes +#DNSSEC=no diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c new file mode 100644 index 0000000000..0b2ffeeddd --- /dev/null +++ b/src/resolve/test-dnssec.c @@ -0,0 +1,217 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 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 <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include "alloc-util.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-rr.h" +#include "string-util.h" + +static void test_dnssec_verify_rrset(void) { + + static const uint8_t signature_blob[] = { + 0x7f, 0x79, 0xdd, 0x5e, 0x89, 0x79, 0x18, 0xd0, 0x34, 0x86, 0x8c, 0x72, 0x77, 0x75, 0x48, 0x4d, + 0xc3, 0x7d, 0x38, 0x04, 0xab, 0xcd, 0x9e, 0x4c, 0x82, 0xb0, 0x92, 0xca, 0xe9, 0x66, 0xe9, 0x6e, + 0x47, 0xc7, 0x68, 0x8c, 0x94, 0xf6, 0x69, 0xcb, 0x75, 0x94, 0xe6, 0x30, 0xa6, 0xfb, 0x68, 0x64, + 0x96, 0x1a, 0x84, 0xe1, 0xdc, 0x16, 0x4c, 0x83, 0x6c, 0x44, 0xf2, 0x74, 0x4d, 0x74, 0x79, 0x8f, + 0xf3, 0xf4, 0x63, 0x0d, 0xef, 0x5a, 0xe7, 0xe2, 0xfd, 0xf2, 0x2b, 0x38, 0x7c, 0x28, 0x96, 0x9d, + 0xb6, 0xcd, 0x5c, 0x3b, 0x57, 0xe2, 0x24, 0x78, 0x65, 0xd0, 0x9e, 0x77, 0x83, 0x09, 0x6c, 0xff, + 0x3d, 0x52, 0x3f, 0x6e, 0xd1, 0xed, 0x2e, 0xf9, 0xee, 0x8e, 0xa6, 0xbe, 0x9a, 0xa8, 0x87, 0x76, + 0xd8, 0x77, 0xcc, 0x96, 0xa0, 0x98, 0xa1, 0xd1, 0x68, 0x09, 0x43, 0xcf, 0x56, 0xd9, 0xd1, 0x66, + }; + + static const uint8_t dnskey_blob[] = { + 0x03, 0x01, 0x00, 0x01, 0x9b, 0x49, 0x9b, 0xc1, 0xf9, 0x9a, 0xe0, 0x4e, 0xcf, 0xcb, 0x14, 0x45, + 0x2e, 0xc9, 0xf9, 0x74, 0xa7, 0x18, 0xb5, 0xf3, 0xde, 0x39, 0x49, 0xdf, 0x63, 0x33, 0x97, 0x52, + 0xe0, 0x8e, 0xac, 0x50, 0x30, 0x8e, 0x09, 0xd5, 0x24, 0x3d, 0x26, 0xa4, 0x49, 0x37, 0x2b, 0xb0, + 0x6b, 0x1b, 0xdf, 0xde, 0x85, 0x83, 0xcb, 0x22, 0x4e, 0x60, 0x0a, 0x91, 0x1a, 0x1f, 0xc5, 0x40, + 0xb1, 0xc3, 0x15, 0xc1, 0x54, 0x77, 0x86, 0x65, 0x53, 0xec, 0x10, 0x90, 0x0c, 0x91, 0x00, 0x5e, + 0x15, 0xdc, 0x08, 0x02, 0x4c, 0x8c, 0x0d, 0xc0, 0xac, 0x6e, 0xc4, 0x3e, 0x1b, 0x80, 0x19, 0xe4, + 0xf7, 0x5f, 0x77, 0x51, 0x06, 0x87, 0x61, 0xde, 0xa2, 0x18, 0x0f, 0x40, 0x8b, 0x79, 0x72, 0xfa, + 0x8d, 0x1a, 0x44, 0x47, 0x0d, 0x8e, 0x3a, 0x2d, 0xc7, 0x39, 0xbf, 0x56, 0x28, 0x97, 0xd9, 0x20, + 0x4f, 0x00, 0x51, 0x3b, + }; + + _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; + + 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); + + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); + assert_se(rrsig); + + rrsig->rrsig.type_covered = DNS_TYPE_A; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 600; + rrsig->rrsig.expiration = 0x5683135c; + rrsig->rrsig.inception = 0x565b7da8; + rrsig->rrsig.key_tag = 63876; + 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); + + assert_se(dns_resource_record_to_string(rrsig, &y) >= 0); + log_info("RRSIG: %s", y); + + 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); + + assert_se(dns_resource_record_to_string(dnskey, &z) >= 0); + log_info("DNSKEY: %s", z); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + + assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0); + + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, a, 0) >= 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); +} + +static void test_dnssec_verify_dns_key(void) { + + static const uint8_t ds1_fprint[] = { + 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, + 0x80, 0x67, 0x14, 0x01, + }; + static const uint8_t ds2_fprint[] = { + 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, + 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + }; + static const uint8_t dnskey_blob[] = { + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, + 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, + 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, + 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, + 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, + 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, + 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, + 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, + 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, + 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, + 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, + 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, + 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, + 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, + 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, + 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, + 0xe7, 0xea, 0x77, 0x03, + }; + + _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"); + assert_se(ds1); + + ds1->ds.key_tag = 47857; + ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; + ds1->ds.digest_size = sizeof(ds1_fprint); + 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); + + ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); + assert_se(ds2); + + ds2->ds.key_tag = 47857; + ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; + ds2->ds.digest_size = sizeof(ds2_fprint); + 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); + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); + assert_se(dnskey); + + dnskey->dnskey.flags = 257; + 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); + + assert_se(dns_resource_record_to_string(dnskey, &c) >= 0); + log_info("DNSKEY: %s", c); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + + assert_se(dnssec_verify_dnskey(dnskey, ds1) > 0); + assert_se(dnssec_verify_dnskey(dnskey, ds2) > 0); +} + +static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { + char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; + + assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); + if (r < 0) + return; + + assert_se(streq(canonicalized, canonical)); +} + +static void test_dnssec_canonicalize(void) { + test_dnssec_canonicalize_one("", ".", 1); + test_dnssec_canonicalize_one(".", ".", 1); + test_dnssec_canonicalize_one("foo", "foo.", 4); + test_dnssec_canonicalize_one("foo.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); + test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); +} + +int main(int argc, char*argv[]) { + + test_dnssec_canonicalize(); + test_dnssec_verify_dns_key(); + test_dnssec_verify_rrset(); + + return 0; +} diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index 30e03c0652..dcdef50a18 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -19,9 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <fcntl.h> +#include <stddef.h> #include <stdint.h> -#include <stdio.h> #include <string.h> #include <unistd.h> @@ -30,7 +31,6 @@ #include "fd-util.h" #include "fileio.h" #include "time-util.h" -#include "util.h" struct acpi_table_header { char signature[4]; diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c index f6ac43adfe..f8cbb333d5 100644 --- a/src/shared/apparmor-util.c +++ b/src/shared/apparmor-util.c @@ -19,11 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stddef.h> + #include "alloc-util.h" #include "apparmor-util.h" #include "fileio.h" #include "parse-util.h" -#include "util.h" bool mac_apparmor_use(void) { static int cached_use = -1; diff --git a/src/shared/architecture.c b/src/shared/architecture.c index 73937bd5a7..ca6821b4d8 100644 --- a/src/shared/architecture.c +++ b/src/shared/architecture.c @@ -22,6 +22,7 @@ #include <sys/utsname.h> #include "architecture.h" +#include "macro.h" #include "string-table.h" #include "string-util.h" diff --git a/src/shared/architecture.h b/src/shared/architecture.h index 61d067cad7..c6af4a5b33 100644 --- a/src/shared/architecture.h +++ b/src/shared/architecture.h @@ -23,6 +23,7 @@ #include <endian.h> +#include "macro.h" #include "util.h" /* A cleaned up architecture definition. We don't want to get lost in diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index fbe2b6fecb..bc12f89f02 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -21,13 +21,22 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> +#include <limits.h> #include <poll.h> +#include <signal.h> #include <stdbool.h> #include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/inotify.h> #include <sys/signalfd.h> #include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/uio.h> #include <sys/un.h> #include <termios.h> #include <unistd.h> @@ -38,6 +47,8 @@ #include "fileio.h" #include "formats-util.h" #include "io-util.h" +#include "log.h" +#include "macro.h" #include "missing.h" #include "mkdir.h" #include "random-util.h" @@ -46,6 +57,7 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "time-util.h" #include "umask-util.h" #include "util.h" diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index e605490c32..2a7a38dd14 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -20,8 +20,11 @@ ***/ #include <errno.h> +#include <fcntl.h> +#include <stdbool.h> #include <stdlib.h> #include <sys/stat.h> +#include <syslog.h> #include <unistd.h> #include "alloc-util.h" diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c index 879aca9374..63daf932f0 100644 --- a/src/shared/boot-timestamps.c +++ b/src/shared/boot-timestamps.c @@ -23,6 +23,8 @@ #include "acpi-fpdt.h" #include "boot-timestamps.h" #include "efivars.h" +#include "macro.h" +#include "time-util.h" int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) { usec_t x = 0, y = 0, a; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 6c24150326..5c6dc34700 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -19,14 +19,24 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/resource.h> #include <sys/socket.h> +#include <unistd.h> +#include "sd-bus-protocol.h" #include "sd-bus.h" #include "sd-daemon.h" #include "sd-event.h" +#include "sd-id128.h" #include "alloc-util.h" -#include "bus-error.h" #include "bus-internal.h" #include "bus-label.h" #include "bus-message.h" @@ -35,7 +45,12 @@ #include "def.h" #include "env-util.h" #include "escape.h" +#include "extract-word.h" #include "fd-util.h" +#include "hashmap.h" +#include "install.h" +#include "kdbus.h" +#include "log.h" #include "macro.h" #include "missing.h" #include "parse-util.h" @@ -49,6 +64,7 @@ #include "string-util.h" #include "strv.h" #include "syslog-util.h" +#include "time-util.h" #include "unit-name.h" #include "user-util.h" #include "utf8.h" @@ -2365,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 diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index ec731d375e..a5e3b6a0b5 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -21,11 +21,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +#include "sd-bus-vtable.h" #include "sd-bus.h" #include "sd-event.h" #include "hashmap.h" #include "install.h" +#include "macro.h" #include "string-util.h" #include "time-util.h" diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 129ffc7056..d256b5a7cc 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -21,7 +21,9 @@ #include <dirent.h> #include <errno.h> +#include <stddef.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "alloc-util.h" @@ -31,11 +33,11 @@ #include "formats-util.h" #include "locale-util.h" #include "macro.h" +#include "output-mode.h" #include "path-util.h" #include "process-util.h" #include "string-util.h" #include "terminal-util.h" -#include "util.h" static int compare(const void *a, const void *b) { const pid_t *p = a, *q = b; diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 5842bdd15e..24b758658d 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -25,6 +25,7 @@ #include <sys/types.h> #include "logs-show.h" +#include "output-mode.h" int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index 71cc613704..2c494d3a31 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -20,22 +20,29 @@ ***/ #include <dirent.h> +#include <errno.h> #include <fcntl.h> +#include <limits.h> #include <mqueue.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/stat.h> +#include <unistd.h> #include "clean-ipc.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" +#include "log.h" +#include "macro.h" #include "string-util.h" #include "strv.h" -#include "util.h" static int clean_sysvipc_shm(uid_t delete_uid) { _cleanup_fclose_ FILE *f = NULL; diff --git a/src/shared/condition.c b/src/shared/condition.c index 14d18429b6..dedaf2291f 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -20,9 +20,13 @@ ***/ #include <errno.h> +#include <fcntl.h> #include <fnmatch.h> +#include <limits.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <time.h> #include <unistd.h> #include "sd-id128.h" @@ -38,6 +42,8 @@ #include "glob-util.h" #include "hostname-util.h" #include "ima-util.h" +#include "list.h" +#include "macro.h" #include "mount-util.h" #include "parse-util.h" #include "path-util.h" diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 486122b0fd..2aae49fbce 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -20,15 +20,17 @@ ***/ #include <errno.h> +#include <limits.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - -#include "sd-messages.h" +#include <sys/types.h> #include "alloc-util.h" #include "conf-files.h" #include "conf-parser.h" +#include "extract-word.h" #include "fd-util.h" #include "fs-util.h" #include "log.h" @@ -40,8 +42,8 @@ #include "string-util.h" #include "strv.h" #include "syslog-util.h" +#include "time-util.h" #include "utf8.h" -#include "util.h" int config_item_table_lookup( const void *table, diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 2872b22d9d..027ed209d9 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -21,9 +21,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdbool.h> +#include <stddef.h> #include <stdio.h> +#include <syslog.h> +#include "alloc-util.h" +#include "log.h" #include "macro.h" /* An abstract parser for simple, line based, shallow configuration diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c index ad3c17d5bd..ff583faa6e 100644 --- a/src/shared/dev-setup.c +++ b/src/shared/dev-setup.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "dev-setup.h" #include "label.h" +#include "log.h" #include "path-util.h" #include "user-util.h" #include "util.h" diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 09ec6e70a0..0466857042 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -24,9 +24,18 @@ #include <stringprep.h> #endif +#include <endian.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> + #include "alloc-util.h" #include "dns-domain.h" +#include "hashmap.h" #include "hexdecoct.h" +#include "in-addr-util.h" +#include "macro.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" @@ -53,12 +62,12 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (*n == 0) break; - if (sz <= 0) - return -ENOSPC; - if (r >= DNS_LABEL_MAX) return -EINVAL; + if (sz <= 0) + return -ENOBUFS; + if (*n == '\\') { /* Escaped character */ @@ -185,10 +194,14 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; - if (l > DNS_LABEL_MAX) + /* DNS labels must be between 1 and 63 characters long. A + * zero-length label does not exist. See RFC 2182, Section + * 11. */ + + if (l <= 0 || l > DNS_LABEL_MAX) return -EINVAL; if (sz < 1) - return -ENOSPC; + return -ENOBUFS; assert(p); assert(dest); @@ -198,10 +211,11 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { if (*p == '.' || *p == '\\') { + /* Dot or backslash */ + if (sz < 3) - return -ENOSPC; + return -ENOBUFS; - /* Dot or backslash */ *(q++) = '\\'; *(q++) = *p; @@ -216,7 +230,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { /* Proper character */ if (sz < 2) - return -ENOSPC; + return -ENOBUFS; *(q++) = *p; sz -= 1; @@ -226,7 +240,7 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { /* Everything else */ if (sz < 5) - return -ENOSPC; + return -ENOBUFS; *(q++) = '\\'; *(q++) = '0' + (char) ((uint8_t) *p / 100); @@ -253,7 +267,7 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { assert(p); assert(ret); - if (l > DNS_LABEL_MAX) + if (l <= 0 || l > DNS_LABEL_MAX) return -EINVAL; s = new(char, DNS_LABEL_ESCAPED_MAX); @@ -273,32 +287,52 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { #ifdef HAVE_LIBIDN _cleanup_free_ uint32_t *input = NULL; - size_t input_size; + size_t input_size, l; const char *p; bool contains_8bit = false; + char buffer[DNS_LABEL_MAX+1]; assert(encoded); assert(decoded); - assert(decoded_max >= DNS_LABEL_MAX); + + /* Converts an U-label into an A-label */ if (encoded_size <= 0) - return 0; + return -EINVAL; for (p = encoded; p < encoded + encoded_size; p++) if ((uint8_t) *p > 127) contains_8bit = true; - if (!contains_8bit) + if (!contains_8bit) { + if (encoded_size > DNS_LABEL_MAX) + return -EINVAL; + return 0; + } input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); if (!input) return -ENOMEM; - if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0) + if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0) return -EINVAL; - return strlen(decoded); + l = strlen(buffer); + + /* Verify that the the result is not longer than one DNS label. */ + if (l <= 0 || l > DNS_LABEL_MAX) + return -EINVAL; + if (l > decoded_max) + return -ENOBUFS; + + memcpy(decoded, buffer, l); + + /* If there's room, append a trailing NUL byte, but only then */ + if (decoded_max > l) + decoded[l] = 0; + + return (int) l; #else return 0; #endif @@ -312,11 +346,14 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, uint32_t *output = NULL; size_t w; - /* To be invoked after unescaping */ + /* To be invoked after unescaping. Converts an A-label into an U-label. */ assert(encoded); assert(decoded); + if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX) + return -EINVAL; + if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1) return 0; @@ -336,11 +373,16 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, if (!result) return -ENOMEM; if (w <= 0) - return 0; - if (w+1 > decoded_max) return -EINVAL; + if (w > decoded_max) + return -ENOBUFS; + + memcpy(decoded, result, w); + + /* Append trailing NUL byte if there's space, but only then. */ + if (decoded_max > w) + decoded[w] = 0; - memcpy(decoded, result, w+1); return w; #else return 0; @@ -409,6 +451,9 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { n += r; } + if (n > DNS_HOSTNAME_MAX) + return -EINVAL; + if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; @@ -511,24 +556,32 @@ int dns_name_equal(const char *x, const char *y) { r = dns_label_unescape(&x, la, sizeof(la)); if (r < 0) return r; - - k = dns_label_undo_idna(la, r, la, sizeof(la)); - if (k < 0) - return k; - if (k > 0) - r = k; + if (r > 0) { + k = dns_label_undo_idna(la, r, la, sizeof(la)); + if (k < 0) + return k; + if (k > 0) + r = k; + } q = dns_label_unescape(&y, lb, sizeof(lb)); if (q < 0) return q; - w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); - if (w < 0) - return w; - if (w > 0) - q = w; + if (q > 0) { + w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); + if (w < 0) + return w; + if (w > 0) + q = w; + } + + /* If one name had fewer labels than the other, this + * will show up as empty label here, which the + * strcasecmp() below will properly consider different + * from a non-empty label. */ la[r] = lb[q] = 0; - if (strcasecmp(la, lb)) + if (strcasecmp(la, lb) != 0) return false; } } @@ -549,11 +602,13 @@ int dns_name_endswith(const char *name, const char *suffix) { r = dns_label_unescape(&n, ln, sizeof(ln)); if (r < 0) return r; - k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); - if (k < 0) - return k; - if (k > 0) - r = k; + if (r > 0) { + k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); + if (k < 0) + return k; + if (k > 0) + r = k; + } if (!saved_n) saved_n = n; @@ -561,11 +616,13 @@ int dns_name_endswith(const char *name, const char *suffix) { q = dns_label_unescape(&s, ls, sizeof(ls)); if (q < 0) return q; - w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); - if (w < 0) - return w; - if (w > 0) - q = w; + if (q > 0) { + w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); + if (w < 0) + return w; + if (w > 0) + q = w; + } if (r == 0 && q == 0) return true; @@ -605,11 +662,13 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char r = dns_label_unescape(&n, ln, sizeof(ln)); if (r < 0) return r; - k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); - if (k < 0) - return k; - if (k > 0) - r = k; + if (r > 0) { + k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); + if (k < 0) + return k; + if (k > 0) + r = k; + } if (!saved_after) saved_after = n; @@ -617,11 +676,13 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char q = dns_label_unescape(&s, ls, sizeof(ls)); if (q < 0) return q; - w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); - if (w < 0) - return w; - if (w > 0) - q = w; + if (q > 0) { + w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); + if (w < 0) + return w; + if (w > 0) + q = w; + } if (r == 0 && q == 0) break; @@ -812,37 +873,60 @@ bool dns_name_is_single_label(const char *name) { return dns_name_is_root(name); } -/* Encode a domain name according to RFC 1035 Section 3.1 */ -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { - uint8_t *label_length; - uint8_t *out; +/* Encode a domain name according to RFC 1035 Section 3.1, without compression */ +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) { + uint8_t *label_length, *out; int r; - assert_return(buffer, -EINVAL); - assert_return(domain, -EINVAL); - assert_return(domain[0], -EINVAL); + assert(domain); + assert(buffer); out = buffer; do { - /* reserve a byte for label length */ - if (len == 0) + /* Reserve a byte for label length */ + if (len <= 0) return -ENOBUFS; len--; label_length = out; out++; - /* convert and copy a single label */ + /* Convert and copy a single label. Note that + * dns_label_unescape() returns 0 when it hits the end + * of the domain name, which we rely on here to encode + * the trailing NUL byte. */ r = dns_label_unescape(&domain, (char *) out, len); if (r < 0) return r; - /* fill label length, move forward */ + 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'; + } + } + + /* Fill label length, move forward */ *label_length = r; out += r; len -= r; + } while (r != 0); + /* Verify the maximum size of the encoded name. The trailing + * dot + NUL byte account are included this time, hence + * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this + * time. */ + if (out - buffer > DNS_HOSTNAME_MAX + 2) + return -EINVAL; + return out - buffer; } diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 99c72574db..3f8f621802 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -22,12 +22,26 @@ #pragma once +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + #include "hashmap.h" #include "in-addr-util.h" +/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ #define DNS_LABEL_MAX 63 + +/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ #define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) +/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ +#define DNS_HOSTNAME_MAX 253 + +/* Maximum length of a full hostname, on the wire, including the final NUL byte */ +#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255 + 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); @@ -71,7 +85,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *a); bool dns_name_is_root(const char *name); bool dns_name_is_single_label(const char *name); -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); bool dns_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 0d44401cc2..692e8b8338 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -19,17 +19,27 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + #include "alloc-util.h" #include "conf-files.h" #include "dropin.h" #include "escape.h" #include "fd-util.h" #include "fileio-label.h" +#include "hashmap.h" +#include "log.h" +#include "macro.h" #include "mkdir.h" #include "path-util.h" +#include "set.h" #include "string-util.h" #include "strv.h" -#include "util.h" +#include "unit-name.h" int drop_in_file(const char *dir, const char *unit, unsigned level, const char *name, char **_p, char **_q) { diff --git a/src/shared/dropin.h b/src/shared/dropin.h index d4531fca2d..a8d647e990 100644 --- a/src/shared/dropin.h +++ b/src/shared/dropin.h @@ -21,6 +21,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "hashmap.h" #include "macro.h" #include "set.h" #include "unit-name.h" diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 89deeb9b55..13af68d539 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -19,17 +19,27 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> +#include <errno.h> #include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> +#include "sd-id128.h" + #include "alloc-util.h" #include "dirent-util.h" #include "efivars.h" #include "fd-util.h" #include "io-util.h" +#include "macro.h" #include "parse-util.h" #include "stdio-util.h" +#include "time-util.h" #include "utf8.h" #include "util.h" #include "virt.h" diff --git a/src/shared/efivars.h b/src/shared/efivars.h index 5cb4c3af4e..94af9717b0 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -22,6 +22,8 @@ ***/ #include <stdbool.h> +#include <stddef.h> +#include <stdint.h> #include "sd-id128.h" diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 5acfb0191b..9606122345 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -19,9 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <alloca.h> #include <arpa/inet.h> +#include <endian.h> +#include <errno.h> #include <net/if.h> -#include <sys/types.h> +#include <stddef.h> +#include <string.h> +#include <sys/socket.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/nf_nat.h> #include <linux/netfilter/xt_addrtype.h> @@ -29,7 +34,8 @@ #include "alloc-util.h" #include "firewall-util.h" -#include "util.h" +#include "in-addr-util.h" +#include "macro.h" DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h index 93152e3978..463e09bcaf 100644 --- a/src/shared/firewall-util.h +++ b/src/shared/firewall-util.h @@ -21,6 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> +#include <stdint.h> + #include "in-addr-util.h" #ifdef HAVE_LIBIPTC diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index eb2845cddf..d013901973 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -19,9 +19,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <mntent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + #include "alloc-util.h" #include "device-nodes.h" #include "fstab-util.h" +#include "macro.h" #include "mount-util.h" #include "parse-util.h" #include "path-util.h" diff --git a/src/shared/generator.c b/src/shared/generator.c index 9998c64416..37de3f7cb1 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -19,6 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <unistd.h> #include "alloc-util.h" @@ -28,11 +29,13 @@ #include "fileio.h" #include "fstab-util.h" #include "generator.h" +#include "log.h" +#include "macro.h" #include "mkdir.h" -#include "mount-util.h" #include "path-util.h" #include "special.h" #include "string-util.h" +#include "time-util.h" #include "unit-name.h" #include "util.h" diff --git a/src/shared/import-util.c b/src/shared/import-util.c index ddc8c00a2d..29ce732b56 100644 --- a/src/shared/import-util.c +++ b/src/shared/import-util.c @@ -19,9 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <string.h> + #include "alloc-util.h" #include "btrfs-util.h" #include "import-util.h" +#include "log.h" +#include "macro.h" #include "path-util.h" #include "string-table.h" #include "string-util.h" diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 74b909d34d..645b3ce33c 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -19,15 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> -#include "alloc-util.h" #include "formats-util.h" #include "install-printf.h" +#include "install.h" +#include "macro.h" #include "specifier.h" #include "unit-name.h" #include "user-util.h" -#include "util.h" static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { UnitFileInstallInfo *i = userdata; diff --git a/src/shared/install.c b/src/shared/install.c index 17e03e59cd..b37f8922df 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -19,22 +19,31 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <fnmatch.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> #include "alloc-util.h" #include "conf-files.h" #include "conf-parser.h" #include "dirent-util.h" +#include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "install-printf.h" #include "install.h" +#include "log.h" +#include "macro.h" #include "mkdir.h" #include "path-lookup.h" #include "path-util.h" @@ -45,7 +54,6 @@ #include "string-util.h" #include "strv.h" #include "unit-name.h" -#include "util.h" #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 diff --git a/src/shared/install.h b/src/shared/install.h index 45a417df92..5519fbcf8f 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -30,7 +30,10 @@ typedef struct UnitFileChange UnitFileChange; typedef struct UnitFileList UnitFileList; typedef struct UnitFileInstallInfo UnitFileInstallInfo; +#include <stdbool.h> + #include "hashmap.h" +#include "macro.h" #include "path-lookup.h" #include "strv.h" #include "unit-name.h" diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 0d7892ac1e..193dad1943 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -21,9 +21,17 @@ #include <errno.h> #include <fcntl.h> +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> #include <string.h> #include <sys/socket.h> +#include <syslog.h> #include <time.h> +#include <unistd.h> + +#include "sd-id128.h" +#include "sd-journal.h" #include "alloc-util.h" #include "fd-util.h" @@ -34,11 +42,15 @@ #include "journal-internal.h" #include "log.h" #include "logs-show.h" +#include "macro.h" +#include "output-mode.h" #include "parse-util.h" #include "process-util.h" +#include "sparse-endian.h" #include "string-table.h" #include "string-util.h" #include "terminal-util.h" +#include "time-util.h" #include "utf8.h" #include "util.h" diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 98927bbc59..396050936d 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -22,11 +22,15 @@ ***/ #include <stdbool.h> +#include <stddef.h> +#include <stdio.h> #include <sys/types.h> #include "sd-journal.h" +#include "macro.h" #include "output-mode.h" +#include "time-util.h" #include "util.h" int output_journal( diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 2c1da0a40d..2ded0ff698 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -19,10 +19,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> +#include <errno.h> #include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> #include <linux/fs.h> -#include <sys/statfs.h> - #include "alloc-util.h" #include "btrfs-util.h" #include "chattr-util.h" @@ -30,6 +35,10 @@ #include "dirent-util.h" #include "fd-util.h" #include "fs-util.h" +#include "hashmap.h" +#include "lockfile-util.h" +#include "log.h" +#include "macro.h" #include "machine-image.h" #include "mkdir.h" #include "path-util.h" @@ -37,7 +46,9 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "utf8.h" +#include "util.h" #include "xattr-util.h" static const char image_search_path[] = diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 038db7453c..5e9d8f6980 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -21,8 +21,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> +#include <stdint.h> + #include "hashmap.h" #include "lockfile-util.h" +#include "macro.h" #include "time-util.h" typedef enum ImageType { diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 4172a63fd0..23cbd8d600 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -19,10 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <fcntl.h> +#include <linux/loop.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> #include <sys/mount.h> #include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/statfs.h> #include <sys/statvfs.h> -#include <sys/vfs.h> +#include <unistd.h> + +#include "sd-bus-protocol.h" +#include "sd-bus.h" #include "alloc-util.h" #include "btrfs-util.h" @@ -30,7 +43,10 @@ #include "fileio.h" #include "fs-util.h" #include "lockfile-util.h" +#include "log.h" #include "machine-pool.h" +#include "macro.h" +#include "missing.h" #include "mkdir.h" #include "mount-util.h" #include "parse-util.h" @@ -39,7 +55,6 @@ #include "signal-util.h" #include "stat-util.h" #include "string-util.h" -#include "util.h" #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL) #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL) diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h index fe01d3d47c..a1f2c5c626 100644 --- a/src/shared/machine-pool.h +++ b/src/shared/machine-pool.h @@ -21,6 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdint.h> + #include "sd-bus.h" /* Grow the /var/lib/machines directory after each 10MiB written */ diff --git a/src/shared/pager.c b/src/shared/pager.c index d149bc1722..07ce926d75 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -19,7 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/prctl.h> @@ -28,13 +32,13 @@ #include "copy.h" #include "fd-util.h" #include "locale-util.h" +#include "log.h" #include "macro.h" #include "pager.h" #include "process-util.h" #include "signal-util.h" #include "string-util.h" #include "terminal-util.h" -#include "util.h" static pid_t pager_pid = 0; diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 4a82bd18cd..90114001ee 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -26,6 +26,8 @@ #include "alloc-util.h" #include "install.h" +#include "log.h" +#include "macro.h" #include "path-lookup.h" #include "path-util.h" #include "string-util.h" diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index e35c8d3c04..b8036718ba 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -21,6 +21,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> #include "macro.h" typedef struct LookupPaths { diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 2666b8f7e2..e6a7a488c9 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -19,15 +19,27 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <limits.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> #include <sys/epoll.h> #include <sys/ioctl.h> +#include <sys/time.h> #include <termios.h> +#include <unistd.h> + +#include "sd-event.h" #include "alloc-util.h" #include "fd-util.h" +#include "log.h" +#include "macro.h" #include "ptyfwd.h" -#include "util.h" +#include "time-util.h" struct PTYForward { sd_event *event; diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index 9b3214221b..002590d1cf 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -25,6 +25,8 @@ #include "sd-event.h" +#include "macro.h" + typedef struct PTYForward PTYForward; typedef enum PTYForwardFlags { diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 09baf51661..bd1d44a0ab 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -19,11 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <seccomp.h> +#include <stddef.h> +#include "macro.h" #include "seccomp-util.h" #include "string-util.h" -#include "util.h" const char* seccomp_arch_to_string(uint32_t c) { diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 60d97154ec..79ee8c728d 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -22,6 +22,7 @@ ***/ #include <seccomp.h> +#include <stdint.h> const char* seccomp_arch_to_string(uint32_t c); int seccomp_arch_from_string(const char *n, uint32_t *ret); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 39b836d053..7ba11e2f0e 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -19,7 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> #include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> #include "alloc-util.h" #include "conf-parser.h" @@ -27,11 +33,11 @@ #include "fd-util.h" #include "fileio.h" #include "log.h" +#include "macro.h" #include "parse-util.h" #include "sleep-config.h" #include "string-util.h" #include "strv.h" -#include "util.h" #define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0) diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 8ea6cb830b..ada4bdb17e 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -28,9 +28,11 @@ #include "fd-util.h" #include "io-util.h" #include "log.h" +#include "macro.h" #include "process-util.h" #include "spawn-polkit-agent.h" #include "stdio-util.h" +#include "time-util.h" #include "util.h" #ifdef ENABLE_POLKIT diff --git a/src/shared/specifier.c b/src/shared/specifier.c index c5c4a4d7d7..841f4654b0 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -19,15 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> #include <string.h> #include <sys/utsname.h> +#include "sd-id128.h" + #include "alloc-util.h" #include "hostname-util.h" #include "macro.h" #include "specifier.h" #include "string-util.h" -#include "util.h" /* * Generic infrastructure for replacing %x style specifiers in diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index fc885f6cb8..b1bbbdaadd 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -21,14 +21,16 @@ #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <stdbool.h> -#include <string.h> +#include <stdio.h> #include <sys/mount.h> #include <sys/stat.h> #include <unistd.h> #include "base-filesystem.h" #include "fd-util.h" +#include "log.h" #include "missing.h" #include "mkdir.h" #include "path-util.h" diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h index adf893a922..1350fd9b1c 100644 --- a/src/shared/switch-root.h +++ b/src/shared/switch-root.h @@ -2,6 +2,7 @@ #pragma once +#include <stdbool.h> /*** This file is part of systemd. diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 70caa542e7..a2cb6e9763 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -19,19 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <errno.h> -#include <getopt.h> -#include <limits.h> -#include <stdbool.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> #include "fileio.h" #include "log.h" +#include "macro.h" #include "string-util.h" #include "sysctl-util.h" -#include "util.h" char *sysctl_normalize(char *s) { char *n; diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index 079dd8752c..1ecef5a44c 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -19,9 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "macro.h" #include "uid-range.h" #include "user-util.h" -#include "util.h" static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) { assert(range); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 13b32a0509..e72f6fa1a2 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -22,7 +22,11 @@ #include <errno.h> #include <fcntl.h> #include <poll.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/time.h> #include <sys/utsname.h> #include <unistd.h> #include <utmpx.h> @@ -34,7 +38,9 @@ #include "path-util.h" #include "string-util.h" #include "terminal-util.h" +#include "time-util.h" #include "user-util.h" +#include "util.h" #include "utmp-wtmp.h" int utmp_get_runlevel(int *runlevel, int *previous) { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index e0ceb873ac..3aec3f959d 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -21,6 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> +#include <sys/types.h> + +#include "time-util.h" #include "util.h" #ifdef HAVE_UTMP diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 7131e94cdb..bc171817ea 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -22,11 +22,13 @@ #include <errno.h> #include <fcntl.h> #include <sys/ioctl.h> +#include <syslog.h> #include <unistd.h> #include <linux/watchdog.h> #include "fd-util.h" #include "log.h" +#include "time-util.h" #include "watchdog.h" static int watchdog_fd = -1; diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h index b748b15857..fd1c11a644 100644 --- a/src/shared/watchdog.h +++ b/src/shared/watchdog.h @@ -21,6 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> + +#include "time-util.h" #include "util.h" int watchdog_set_timeout(usec_t *usec); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index f010e4e19a..de003e251c 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -39,7 +39,7 @@ static void test_dns_label_unescape_one(const char *what, const char *expect, si static void test_dns_label_unescape(void) { test_dns_label_unescape_one("hallo", "hallo", 6, 5); - test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC); + test_dns_label_unescape_one("hallo", "hallo", 4, -ENOBUFS); test_dns_label_unescape_one("", "", 10, 0); test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12); test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5); @@ -56,7 +56,7 @@ static void test_dns_name_to_wire_format_one(const char *what, const char *expec uint8_t buffer[buffer_sz]; int r; - r = dns_name_to_wire_format(what, buffer, buffer_sz); + r = dns_name_to_wire_format(what, buffer, buffer_sz, false); assert_se(r == ret); if (r < 0) @@ -66,11 +66,38 @@ static void test_dns_name_to_wire_format_one(const char *what, const char *expec } static void test_dns_name_to_wire_format(void) { - const char out1[] = { 3, 'f', 'o', 'o', 0 }; - const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; - const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; - - test_dns_name_to_wire_format_one("", NULL, 0, -EINVAL); + static const char out0[] = { 0 }; + static const char out1[] = { 3, 'f', 'o', 'o', 0 }; + static const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + static const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + static const char out4[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8', + 3, 'a', '1', '2', 0 }; + + test_dns_name_to_wire_format_one("", out0, sizeof(out0), sizeof(out0)); test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1)); test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1)); @@ -80,6 +107,9 @@ static void test_dns_name_to_wire_format(void) { test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL); test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3)); + + test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", NULL, 500, -EINVAL); + test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", out4, sizeof(out4), sizeof(out4)); } static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) { @@ -102,7 +132,7 @@ static void test_dns_label_unescape_suffix_one(const char *what, const char *exp static void test_dns_label_unescape_suffix(void) { test_dns_label_unescape_suffix_one("hallo", "hallo", "", 6, 5, 0); - test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOSPC, -ENOSPC); + test_dns_label_unescape_suffix_one("hallo", "hallo", "", 4, -ENOBUFS, -ENOBUFS); test_dns_label_unescape_suffix_one("", "", "", 10, 0, 0); test_dns_label_unescape_suffix_one("hallo\\.foobar", "hallo.foobar", "", 20, 12, 0); test_dns_label_unescape_suffix_one("hallo.foobar", "foobar", "hallo", 10, 6, 5); @@ -136,7 +166,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, "", 0); + 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 hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31); @@ -314,6 +344,24 @@ static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("\\zbar", 0); test_dns_name_is_valid_one("ä", 1); test_dns_name_is_valid_one("\n", 0); + + /* 256 characters*/ + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0); + + /* 255 characters*/ + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0); + + /* 254 characters*/ + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0); + + /* 253 characters*/ + test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1); + + /* label of 64 chars length */ + test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a123", 0); + + /* label of 63 chars length */ + test_dns_name_is_valid_one("a123456789a123456789a123456789a123456789a123456789a123456789a12", 1); } static void test_dns_service_name_is_valid(void) { diff --git a/src/test/test-engine.c b/src/test/test-engine.c index c98d01168c..e23eec7370 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -25,6 +25,7 @@ #include "bus-util.h" #include "manager.h" +#include "test-helper.h" int main(int argc, char *argv[]) { _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; @@ -38,8 +39,8 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(MANAGER_USER, true, &m); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { - printf("Skipping test: manager_new: %s", strerror(-r)); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 03ec0fcfc7..753afadb0a 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -29,6 +29,7 @@ #include "mkdir.h" #include "path-util.h" #include "rm-rf.h" +#include "test-helper.h" #include "unit.h" #include "util.h" @@ -296,8 +297,8 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR3") == 0); r = manager_new(MANAGER_USER, true, &m); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { - printf("Skipping test: manager_new: %s", strerror(-r)); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-helper.h b/src/test/test-helper.h index f75dd3374a..c0f6a91787 100644 --- a/src/test/test-helper.h +++ b/src/test/test-helper.h @@ -23,9 +23,21 @@ #include "sd-daemon.h" +#include "macro.h" + #define TEST_REQ_RUNNING_SYSTEMD(x) \ if (sd_booted() > 0) { \ x; \ } else { \ printf("systemd not booted skipping '%s'\n", #x); \ } + +#define MANAGER_SKIP_TEST(r) \ + IN_SET(r, \ + -EPERM, \ + -EACCES, \ + -EADDRINUSE, \ + -EHOSTDOWN, \ + -ENOENT, \ + -ENOMEDIUM /* cannot determine cgroup */ \ + ) diff --git a/src/test/test-path.c b/src/test/test-path.c index 8302bdd283..7a3b145414 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -29,6 +29,7 @@ #include "rm-rf.h" #include "string-util.h" #include "strv.h" +#include "test-helper.h" #include "unit.h" #include "util.h" @@ -44,8 +45,8 @@ static int setup_test(Manager **m) { assert_se(m); r = manager_new(MANAGER_USER, true, &tmp); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { - printf("Skipping test: manager_new: %s", strerror(-r)); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); return -EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c new file mode 100644 index 0000000000..8ae416c557 --- /dev/null +++ b/src/test/test-rbtree.c @@ -0,0 +1,362 @@ +/*** + This file is part of systemd. See COPYING for details. + + 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/>. +***/ + +/* + * Tests for RB-Tree + */ + +#undef NDEBUG +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> +#include "c-rbtree.h" + +/* verify that all API calls are exported */ +static void test_api(void) { + CRBTree t = {}; + CRBNode n = C_RBNODE_INIT(n); + + assert(!c_rbnode_is_linked(&n)); + + /* init, is_linked, add, remove, remove_init */ + + c_rbtree_add(&t, NULL, &t.root, &n); + assert(c_rbnode_is_linked(&n)); + + c_rbtree_remove_init(&t, &n); + assert(!c_rbnode_is_linked(&n)); + + c_rbtree_add(&t, NULL, &t.root, &n); + assert(c_rbnode_is_linked(&n)); + + c_rbtree_remove(&t, &n); + assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */ + + c_rbnode_init(&n); + assert(!c_rbnode_is_linked(&n)); + + /* first, last, leftmost, rightmost, next, prev */ + + assert(!c_rbtree_first(&t)); + assert(!c_rbtree_last(&t)); + assert(&n == c_rbnode_leftmost(&n)); + assert(&n == c_rbnode_rightmost(&n)); + assert(!c_rbnode_next(&n)); + assert(!c_rbnode_prev(&n)); +} + +/* copied from c-rbtree.c, relies on internal representation */ +static inline _Bool c_rbnode_is_red(CRBNode *n) { + return !((unsigned long)n->__parent_and_color & 1UL); +} + +/* copied from c-rbtree.c, relies on internal representation */ +static inline _Bool c_rbnode_is_black(CRBNode *n) { + return !!((unsigned long)n->__parent_and_color & 1UL); +} + +static size_t validate(CRBTree *t) { + unsigned int i_black, n_black; + CRBNode *n, *p, *o; + size_t count = 0; + + assert(t); + assert(!t->root || c_rbnode_is_black(t->root)); + + /* traverse to left-most child, count black nodes */ + i_black = 0; + n = t->root; + while (n && n->left) { + if (c_rbnode_is_black(n)) + ++i_black; + n = n->left; + } + n_black = i_black; + + /* + * Traverse tree and verify correctness: + * 1) A node is either red or black + * 2) The root is black + * 3) All leaves are black + * 4) Every red node must have two black child nodes + * 5) Every path to a leaf contains the same number of black nodes + * + * Note that NULL nodes are considered black, which is why we don't + * check for 3). + */ + o = NULL; + while (n) { + ++count; + + /* verify natural order */ + assert(n > o); + o = n; + + /* verify consistency */ + assert(!n->right || c_rbnode_parent(n->right) == n); + assert(!n->left || c_rbnode_parent(n->left) == n); + + /* verify 2) */ + if (!c_rbnode_parent(n)) + assert(c_rbnode_is_black(n)); + + if (c_rbnode_is_red(n)) { + /* verify 4) */ + assert(!n->left || c_rbnode_is_black(n->left)); + assert(!n->right || c_rbnode_is_black(n->right)); + } else { + /* verify 1) */ + assert(c_rbnode_is_black(n)); + } + + /* verify 5) */ + if (!n->left && !n->right) + assert(i_black == n_black); + + /* get next node */ + if (n->right) { + n = n->right; + if (c_rbnode_is_black(n)) + ++i_black; + + while (n->left) { + n = n->left; + if (c_rbnode_is_black(n)) + ++i_black; + } + } else { + while ((p = c_rbnode_parent(n)) && n == p->right) { + n = p; + if (c_rbnode_is_black(p->right)) + --i_black; + } + + n = p; + if (p && c_rbnode_is_black(p->left)) + --i_black; + } + } + + return count; +} + +static void insert(CRBTree *t, CRBNode *n) { + CRBNode **i, *p; + + assert(t); + assert(n); + assert(!c_rbnode_is_linked(n)); + + i = &t->root; + p = NULL; + while (*i) { + p = *i; + if (n < *i) { + i = &(*i)->left; + } else { + assert(n > *i); + i = &(*i)->right; + } + } + + c_rbtree_add(t, p, i, n); +} + +static void shuffle(void **nodes, size_t n_memb) { + unsigned int i, j; + void *t; + + for (i = 0; i < n_memb; ++i) { + j = rand() % n_memb; + t = nodes[j]; + nodes[j] = nodes[i]; + nodes[i] = t; + } +} + +/* run some pseudo-random tests on the tree */ +static void test_shuffle(void) { + CRBNode *nodes[256]; + CRBTree t = {}; + unsigned int i, j; + size_t n; + + /* allocate and initialize all nodes */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + nodes[i] = malloc(sizeof(*nodes[i])); + assert(nodes[i]); + c_rbnode_init(nodes[i]); + } + + /* shuffle nodes and validate *empty* tree */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + n = validate(&t); + assert(n == 0); + + /* add all nodes and validate after each insertion */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + insert(&t, nodes[i]); + n = validate(&t); + assert(n == i + 1); + } + + /* shuffle nodes again */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + + /* remove all nodes (in different order) and validate on each round */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + c_rbtree_remove(&t, nodes[i]); + n = validate(&t); + assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); + c_rbnode_init(nodes[i]); + } + + /* shuffle nodes and validate *empty* tree again */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + n = validate(&t); + assert(n == 0); + + /* add all nodes again */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + insert(&t, nodes[i]); + n = validate(&t); + assert(n == i + 1); + } + + /* 4 times, remove half of the nodes and add them again */ + for (j = 0; j < 4; ++j) { + /* shuffle nodes again */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + + /* remove half of the nodes */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { + c_rbtree_remove(&t, nodes[i]); + n = validate(&t); + assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); + c_rbnode_init(nodes[i]); + } + + /* shuffle the removed half */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2); + + /* add the removed half again */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { + insert(&t, nodes[i]); + n = validate(&t); + assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1); + } + } + + /* shuffle nodes again */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + + /* remove all */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + c_rbtree_remove(&t, nodes[i]); + n = validate(&t); + assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); + c_rbnode_init(nodes[i]); + } + + /* free nodes again */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) + free(nodes[i]); +} + +typedef struct { + unsigned long key; + CRBNode rb; +} Node; + +#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb))) + +static int compare(CRBTree *t, void *k, CRBNode *n) { + unsigned long key = (unsigned long)k; + Node *node = node_from_rb(n); + + return (key < node->key) ? -1 : (key > node->key) ? 1 : 0; +} + +/* run tests against the c_rbtree_find*() helpers */ +static void test_map(void) { + CRBNode **slot, *p; + CRBTree t = {}; + Node *nodes[2048]; + unsigned long i; + + /* allocate and initialize all nodes */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + nodes[i] = malloc(sizeof(*nodes[i])); + assert(nodes[i]); + nodes[i]->key = i; + c_rbnode_init(&nodes[i]->rb); + } + + /* shuffle nodes */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + + /* add all nodes, and verify that each node is linked */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + assert(!c_rbnode_is_linked(&nodes[i]->rb)); + assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); + + slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p); + assert(slot); + c_rbtree_add(&t, p, slot, &nodes[i]->rb); + + assert(c_rbnode_is_linked(&nodes[i]->rb)); + assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); + } + + /* shuffle nodes again */ + shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); + + /* remove all nodes (in different order) */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { + assert(c_rbnode_is_linked(&nodes[i]->rb)); + assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); + + c_rbtree_remove_init(&t, &nodes[i]->rb); + + assert(!c_rbnode_is_linked(&nodes[i]->rb)); + assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); + } + + /* free nodes again */ + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) + free(nodes[i]); +} + +int main(int argc, char **argv) { + unsigned int i; + + /* we want stable tests, so use fixed seed */ + srand(0xdeadbeef); + + test_api(); + + /* + * The tests are pseudo random; run them multiple times, each run will + * have different orders and thus different results. + */ + for (i = 0; i < 4; ++i) { + test_shuffle(); + test_map(); + } + + return 0; +} diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c new file mode 100644 index 0000000000..00d3ecc0de --- /dev/null +++ b/src/test/test-rlimit-util.c @@ -0,0 +1,69 @@ +/*** + This file is part of systemd + + 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 <sys/resource.h> + +#include "capability-util.h" +#include "macro.h" +#include "rlimit-util.h" +#include "string-util.h" +#include "util.h" + +int main(int argc, char *argv[]) { + struct rlimit old, new, high; + struct rlimit err = { + .rlim_cur = 10, + .rlim_max = 5, + }; + + log_parse_environment(); + log_open(); + + assert_se(drop_capability(CAP_SYS_RESOURCE) == 0); + + assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); + new.rlim_cur = MIN(5U, old.rlim_max); + new.rlim_max = MIN(10U, old.rlim_max); + assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0); + + assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1); + + assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "LimitNOFILE")); + assert_se(rlimit_to_string(-1) == NULL); + + assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); + assert_se(setrlimit_closest(RLIMIT_NOFILE, &old) == 0); + assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0); + assert_se(old.rlim_cur == new.rlim_cur); + assert_se(old.rlim_max == new.rlim_max); + + assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); + high = RLIMIT_MAKE_CONST(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); + assert_se(new.rlim_cur == new.rlim_max); + + assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); + assert_se(setrlimit_closest(RLIMIT_NOFILE, &err) == -EINVAL); + assert_se(getrlimit(RLIMIT_NOFILE, &new) == 0); + assert_se(old.rlim_cur == new.rlim_cur); + assert_se(old.rlim_max == new.rlim_max); + + return 0; +} diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 8396ae60f3..60b5160cec 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -23,6 +23,7 @@ #include "macro.h" #include "manager.h" +#include "test-helper.h" int main(int argc, char *argv[]) { Manager *m = NULL; @@ -35,8 +36,8 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(MANAGER_USER, true, &m); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { - printf("Skipping test: manager_new: %s", strerror(-r)); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; } assert_se(r >= 0); diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h index 3f99ae7724..1054551d0b 100644 --- a/src/udev/scsi_id/scsi.h +++ b/src/udev/scsi_id/scsi.h @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * scsi.h * @@ -24,25 +26,25 @@ struct scsi_ioctl_command { /* * Default 5 second timeout */ -#define DEF_TIMEOUT 5000 +#define DEF_TIMEOUT 5000 -#define SENSE_BUFF_LEN 32 +#define SENSE_BUFF_LEN 32 /* * The request buffer size passed to the SCSI INQUIRY commands, use 254, * as this is a nice value for some devices, especially some of the usb * mass storage devices. */ -#define SCSI_INQ_BUFF_LEN 254 +#define SCSI_INQ_BUFF_LEN 254 /* * SCSI INQUIRY vendor and model (really product) lengths. */ -#define VENDOR_LENGTH 8 -#define MODEL_LENGTH 16 +#define VENDOR_LENGTH 8 +#define MODEL_LENGTH 16 -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 /* * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the @@ -52,34 +54,34 @@ struct scsi_ioctl_command { /* * id type values of id descriptors. These are assumed to fit in 4 bits. */ -#define SCSI_ID_VENDOR_SPECIFIC 0 -#define SCSI_ID_T10_VENDOR 1 -#define SCSI_ID_EUI_64 2 -#define SCSI_ID_NAA 3 -#define SCSI_ID_RELPORT 4 +#define SCSI_ID_VENDOR_SPECIFIC 0 +#define SCSI_ID_T10_VENDOR 1 +#define SCSI_ID_EUI_64 2 +#define SCSI_ID_NAA 3 +#define SCSI_ID_RELPORT 4 #define SCSI_ID_TGTGROUP 5 #define SCSI_ID_LUNGROUP 6 -#define SCSI_ID_MD5 7 -#define SCSI_ID_NAME 8 +#define SCSI_ID_MD5 7 +#define SCSI_ID_NAME 8 /* * Supported NAA values. These fit in 4 bits, so the "don't care" value * cannot conflict with real values. */ -#define SCSI_ID_NAA_DONT_CARE 0xff -#define SCSI_ID_NAA_IEEE_REG 5 -#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6 +#define SCSI_ID_NAA_DONT_CARE 0xff +#define SCSI_ID_NAA_IEEE_REG 0x05 +#define SCSI_ID_NAA_IEEE_REG_EXTENDED 0x06 /* * Supported Code Set values. */ -#define SCSI_ID_BINARY 1 -#define SCSI_ID_ASCII 2 +#define SCSI_ID_BINARY 1 +#define SCSI_ID_ASCII 2 struct scsi_id_search_values { - u_char id_type; - u_char naa_type; - u_char code_set; + u_char id_type; + u_char naa_type; + u_char code_set; }; /* @@ -87,13 +89,13 @@ struct scsi_id_search_values { * used a 1 bit right and masked version of these. So now CHECK_CONDITION * and friends (in <scsi/scsi.h>) are deprecated. */ -#define SCSI_CHECK_CONDITION 0x2 -#define SCSI_CONDITION_MET 0x4 -#define SCSI_BUSY 0x8 -#define SCSI_IMMEDIATE 0x10 +#define SCSI_CHECK_CONDITION 0x02 +#define SCSI_CONDITION_MET 0x04 +#define SCSI_BUSY 0x08 +#define SCSI_IMMEDIATE 0x10 #define SCSI_IMMEDIATE_CONDITION_MET 0x14 -#define SCSI_RESERVATION_CONFLICT 0x18 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SCSI_TASK_SET_FULL 0x28 -#define SCSI_ACA_ACTIVE 0x30 -#define SCSI_TASK_ABORTED 0x40 +#define SCSI_RESERVATION_CONFLICT 0x18 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SCSI_TASK_SET_FULL 0x28 +#define SCSI_ACA_ACTIVE 0x30 +#define SCSI_TASK_ABORTED 0x40 diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 4655691642..e9ab7dce59 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) IBM Corp. 2003 * Copyright (C) SUSE Linux Products GmbH, 2006 diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h index 141b116a88..25f3d1a3b7 100644 --- a/src/udev/scsi_id/scsi_id.h +++ b/src/udev/scsi_id/scsi_id.h @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) IBM Corp. 2003 * diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index c7ef783684..bc18af05af 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) IBM Corp. 2003 * @@ -50,11 +52,11 @@ * is normally one or some small number of descriptors. */ static const struct scsi_id_search_values id_search_list[] = { - { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, + { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, /* * Devices already exist using NAA values that are now marked * reserved. These should not conflict with other values, or it is @@ -64,14 +66,14 @@ static const struct scsi_id_search_values id_search_list[] = { * non-IEEE descriptors in a random order will get different * names. */ - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, }; static const char hex_str[]="0123456789abcdef"; @@ -81,21 +83,21 @@ static const char hex_str[]="0123456789abcdef"; * are used here. */ -#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ -#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ -#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ -#define DRIVER_TIMEOUT 0x06 -#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ +#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ +#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ +#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ +#define DRIVER_TIMEOUT 0x06 +#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ /* The following "category" function returns one of the following */ #define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ #define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ #define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ -#define SG_ERR_CAT_TIMEOUT 3 -#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ -#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */ -#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */ -#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ +#define SG_ERR_CAT_TIMEOUT 3 +#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ +#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */ +#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */ +#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ static int do_scsi_page80_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, int fd, @@ -212,7 +214,7 @@ static int scsi_dump_sense(struct udev *udev, s = sense_buffer[7] + 8; if (sb_len < s) { log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, s - sb_len); + dev_scsi->kernel, sb_len, s - sb_len); return -1; } if ((code == 0x0) || (code == 0x1)) { @@ -222,7 +224,7 @@ static int scsi_dump_sense(struct udev *udev, * Possible? */ log_debug("%s: sense result too" " small %d bytes", - dev_scsi->kernel, s); + dev_scsi->kernel, s); return -1; } asc = sense_buffer[12]; @@ -233,15 +235,15 @@ static int scsi_dump_sense(struct udev *udev, ascq = sense_buffer[3]; } else { log_debug("%s: invalid sense code 0x%x", - dev_scsi->kernel, code); + dev_scsi->kernel, code); return -1; } log_debug("%s: sense key 0x%x ASC 0x%x ASCQ 0x%x", - dev_scsi->kernel, sense_key, asc, ascq); + dev_scsi->kernel, sense_key, asc, ascq); } else { if (sb_len < 4) { log_debug("%s: sense buffer too small %d bytes, %d bytes too short", - dev_scsi->kernel, sb_len, 4 - sb_len); + dev_scsi->kernel, sb_len, 4 - sb_len); return -1; } @@ -249,9 +251,9 @@ static int scsi_dump_sense(struct udev *udev, log_debug("%s: old sense key: 0x%x", dev_scsi->kernel, sense_buffer[0] & 0x0f); else log_debug("%s: sense = %2x %2x", - dev_scsi->kernel, sense_buffer[0], sense_buffer[2]); + dev_scsi->kernel, sense_buffer[0], sense_buffer[2]); log_debug("%s: non-extended sense class %d code 0x%0x", - dev_scsi->kernel, sense_class, code); + dev_scsi->kernel, sense_class, code); } @@ -282,7 +284,7 @@ static int scsi_dump(struct udev *udev, } log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x", - dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); + dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); if (io->status == SCSI_CHECK_CONDITION) return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); else @@ -302,8 +304,7 @@ static int scsi_dump_v4(struct udev *udev, } log_debug("%s: sg_io failed status 0x%x 0x%x 0x%x", - dev_scsi->kernel, io->driver_status, io->transport_status, - io->device_status); + dev_scsi->kernel, io->driver_status, io->transport_status, io->device_status); if (io->device_status == SCSI_CHECK_CONDITION) return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, io->response_len); @@ -399,7 +400,7 @@ resend: error: if (retval < 0) log_debug("%s: Unable to get INQUIRY vpd %d page 0x%x.", - dev_scsi->kernel, evpd, page); + dev_scsi->kernel, evpd, page); return retval; } @@ -421,7 +422,7 @@ static int do_scsi_page0_inquiry(struct udev *udev, return 1; } if (buffer[3] > len) { - log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]); + log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]); return 1; } @@ -464,7 +465,7 @@ static int prepend_vendor_model(struct udev *udev, */ if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { log_debug("%s: expected length %d, got length %d", - dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); + dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); return -1; } return ind; @@ -529,7 +530,7 @@ static int check_fill_0x83_id(struct udev *udev, if (max_len < len) { log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); + dev_scsi->kernel, max_len, len); return 1; } @@ -785,7 +786,7 @@ static int do_scsi_page80_inquiry(struct udev *udev, len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; if (max_len < len) { log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); + dev_scsi->kernel, max_len, len); return 1; } /* diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index d0e47ec6d8..0b1ae706e7 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * probe disks for filesystems and partitions * diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c index cfaa463804..467010f5b3 100644 --- a/src/udev/udev-builtin-btrfs.c +++ b/src/udev/udev-builtin-btrfs.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c index f4a065a97d..a9e312e2c0 100644 --- a/src/udev/udev-builtin-hwdb.c +++ b/src/udev/udev-builtin-hwdb.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index fddafbd4dc..1d31829a08 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * expose input properties via udev * diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index aa10beafb0..b80be52567 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c index 9665f678fd..9210d1cc71 100644 --- a/src/udev/udev-builtin-kmod.c +++ b/src/udev/udev-builtin-kmod.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * load kernel modules * diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index bf5c9c6b77..e549fdbee9 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index aa18c7e420..7851cec17f 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * compose persistent device path * diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index 3ebe36f043..b650a15bd8 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * manage device node user ACL * diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 587649eff0..40d1e8cc47 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * USB device properties and persistent device path * diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index e6b36f124f..18fb6615d5 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 962de22f43..10dd747256 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * libudev - interface to udev device information * diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 2ef8bfe59e..c1dcee6c73 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org> * @@ -848,11 +850,11 @@ void udev_event_execute_rules(struct udev_event *event, /* disable watch during event processing */ if (major(udev_device_get_devnum(dev)) != 0) udev_watch_end(event->udev, event->dev_db); - } - if (major(udev_device_get_devnum(dev)) == 0 && - streq(udev_device_get_action(dev), "move")) - udev_device_copy_properties(dev, event->dev_db); + if (major(udev_device_get_devnum(dev)) == 0 && + streq(udev_device_get_action(dev), "move")) + udev_device_copy_properties(dev, event->dev_db); + } udev_rules_apply_to_event(rules, event, timeout_usec, timeout_warn_usec, diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index c2edf2c5cd..39ae2cc1b1 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c06ace09cf..7342f2849e 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2003-2012 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index f1fdccaed8..60de703706 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> * Copyright (C) 2009 Canonical Ltd. diff --git a/src/udev/udev.h b/src/udev/udev.h index 1f9c8120c0..4f4002056c 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org> diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 989decbe95..119033c2af 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2005-2011 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 948ad0f5a5..53f0871957 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 7182668f23..ca67c385b4 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index f9cb5e63a2..1579894082 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2004-2010 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 6a5dc6e9e4..2cc9f123bd 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org> * Copyright (C) 2009 Canonical Ltd. diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index 0b180d03eb..7389ca1b72 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2011 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index ff427cf292..00ad917efc 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2004-2008 Kay Sievers <kay@vrfy.org> diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 9d52345d92..1385b87b3a 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c index 3539c1d6ab..94cbe21f3e 100644 --- a/src/udev/udevadm-util.c +++ b/src/udev/udevadm-util.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h index 37e4fe8369..5882096081 100644 --- a/src/udev/udevadm-util.h +++ b/src/udev/udevadm-util.h @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> * diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 60f122ebda..af1b5a9186 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -1,4 +1,5 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2007-2012 Kay Sievers <kay@vrfy.org> * diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 8b2f5d4e30..366e7fbb87 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2004-2012 Kay Sievers <kay@vrfy.org> * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c index aec6676a33..377eb7a72c 100644 --- a/src/udev/v4l_id/v4l_id.c +++ b/src/udev/v4l_id/v4l_id.c @@ -1,3 +1,5 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /* * Copyright (C) 2009 Kay Sievers <kay@vrfy.org> * Copyright (c) 2009 Filippo Argiolas <filippo.argiolas@gmail.com> diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c index 252cbdb26c..d28b196c4e 100644 --- a/src/user-sessions/user-sessions.c +++ b/src/user-sessions/user-sessions.c @@ -23,7 +23,9 @@ #include <unistd.h> #include "fileio.h" +#include "fileio-label.h" #include "log.h" +#include "selinux-util.h" #include "string-util.h" #include "util.h" @@ -40,6 +42,8 @@ int main(int argc, char*argv[]) { umask(0022); + mac_selinux_init(NULL); + if (streq(argv[1], "start")) { int r = 0; @@ -65,7 +69,7 @@ int main(int argc, char*argv[]) { } else if (streq(argv[1], "stop")) { int r; - r = write_string_file("/run/nologin", "System is going down.", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + r = write_string_file_atomic_label("/run/nologin", "System is going down."); if (r < 0) { log_error_errno(r, "Failed to create /run/nologin: %m"); return EXIT_FAILURE; @@ -76,5 +80,7 @@ int main(int argc, char*argv[]) { return EXIT_FAILURE; } + mac_selinux_finish(); + return EXIT_SUCCESS; } diff --git a/test/TEST-05-RLIMITS/Makefile b/test/TEST-05-RLIMITS/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-05-RLIMITS/Makefile @@ -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 index 0000000000..54000ecefb --- /dev/null +++ b/test/TEST-05-RLIMITS/test-rlimits.sh @@ -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 index 0000000000..6eaa0b8f34 --- /dev/null +++ b/test/TEST-05-RLIMITS/test.sh @@ -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/networkd-test.py b/test/networkd-test.py new file mode 100755 index 0000000000..d76ab507d2 --- /dev/null +++ b/test/networkd-test.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python3 +# +# networkd integration test +# This uses temporary configuration in /run and temporary veth devices, and +# does not write anything on disk or change any system configuration; +# but it assumes (and checks at the beginning) that networkd is not currently +# running. +# This can be run on a normal installation, in QEMU, nspawn, or LXC. +# ATTENTION: This uses the *installed* networkd, not the one from the built +# source tree. +# +# (C) 2015 Canonical Ltd. +# Author: Martin Pitt <martin.pitt@ubuntu.com> +# +# 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/>. + +import os +import sys +import time +import unittest +import tempfile +import subprocess +import shutil + +networkd_active = subprocess.call(['systemctl', 'is-active', '--quiet', + 'systemd-networkd']) == 0 +have_dnsmasq = shutil.which('dnsmasq') + + +@unittest.skipIf(networkd_active, + 'networkd is already active') +class ClientTestBase: + def setUp(self): + self.iface = 'test_eth42' + self.if_router = 'router_eth42' + self.workdir_obj = tempfile.TemporaryDirectory() + self.workdir = self.workdir_obj.name + self.config = '/run/systemd/network/test_eth42.network' + os.makedirs(os.path.dirname(self.config), exist_ok=True) + + # avoid "Failed to open /dev/tty" errors in containers + os.environ['SYSTEMD_LOG_TARGET'] = 'journal' + + # determine path to systemd-networkd-wait-online + for p in ['/usr/lib/systemd/systemd-networkd-wait-online', + '/lib/systemd/systemd-networkd-wait-online']: + if os.path.exists(p): + self.networkd_wait_online = p + break + else: + self.fail('systemd-networkd-wait-online not found') + + # get current journal cursor + out = subprocess.check_output(['journalctl', '-b', '--quiet', + '--no-pager', '-n0', '--show-cursor'], + universal_newlines=True) + self.assertTrue(out.startswith('-- cursor:')) + self.journal_cursor = out.split()[-1] + + def tearDown(self): + self.shutdown_iface() + if os.path.exists(self.config): + os.unlink(self.config) + subprocess.call(['systemctl', 'stop', 'systemd-networkd']) + + def show_journal(self, unit): + '''Show journal of given unit since start of the test''' + + print('---- %s ----' % unit) + sys.stdout.flush() + subprocess.call(['journalctl', '-b', '--no-pager', '--quiet', + '--cursor', self.journal_cursor, '-u', unit]) + + def create_iface(self, ipv6=False): + '''Create test interface with DHCP server behind it''' + + raise NotImplementedError('must be implemented by a subclass') + + def shutdown_iface(self): + '''Remove test interface and stop DHCP server''' + + raise NotImplementedError('must be implemented by a subclass') + + def print_server_log(self): + '''Print DHCP server log for debugging failures''' + + raise NotImplementedError('must be implemented by a subclass') + + def do_test(self, coldplug=True, ipv6=False, extra_opts='', + online_timeout=10, dhcp_mode='yes'): + with open(self.config, 'w') as f: + f.write('''[Match] +Name=%s +[Network] +DHCP=%s +%s''' % (self.iface, dhcp_mode, extra_opts)) + + if coldplug: + # create interface first, then start networkd + self.create_iface(ipv6=ipv6) + subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + else: + # start networkd first, then create interface + subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.create_iface(ipv6=ipv6) + + try: + subprocess.check_call([self.networkd_wait_online, '--interface', + self.iface, '--timeout=%i' % online_timeout]) + + if ipv6: + # check iface state and IP 6 address; FIXME: we need to wait a bit + # longer, as the iface is "configured" already with IPv4 *or* + # IPv6, but we want to wait for both + for timeout in range(10): + out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.iface]) + if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out: + break + time.sleep(1) + else: + self.fail('timed out waiting for IPv6 configuration') + + self.assertRegex(out, b'inet6 2600::.* scope global .*dynamic') + self.assertRegex(out, b'inet6 fe80::.* scope link') + else: + # should have link-local address on IPv6 only + out = subprocess.check_output(['ip', '-6', 'a', 'show', 'dev', self.iface]) + self.assertRegex(out, b'inet6 fe80::.* scope link') + self.assertNotIn(b'scope global', out) + + # should have IPv4 address + out = subprocess.check_output(['ip', '-4', 'a', 'show', 'dev', self.iface]) + self.assertIn(b'state UP', out) + self.assertRegex(out, b'inet 192.168.5.\d+/.* scope global dynamic') + + # check networkctl state + out = subprocess.check_output(['networkctl']) + self.assertRegex(out, ('%s\s+ether\s+routable\s+unmanaged' % self.if_router).encode()) + self.assertRegex(out, ('%s\s+ether\s+routable\s+configured' % self.iface).encode()) + + out = subprocess.check_output(['networkctl', 'status', self.iface]) + self.assertRegex(out, b'Type:\s+ether') + self.assertRegex(out, b'State:\s+routable.*configured') + self.assertRegex(out, b'Address:\s+192.168.5.\d+') + if ipv6: + self.assertRegex(out, b'2600::') + else: + self.assertNotIn(b'2600::', out) + self.assertRegex(out, b'fe80::') + self.assertRegex(out, b'Gateway:\s+192.168.5.1') + self.assertRegex(out, b'DNS:\s+192.168.5.1') + except (AssertionError, subprocess.CalledProcessError): + # show networkd status, journal, and DHCP server log on failure + with open(self.config) as f: + print('\n---- %s ----\n%s' % (self.config, f.read())) + print('---- interface status ----') + sys.stdout.flush() + subprocess.call(['ip', 'a', 'show', 'dev', self.iface]) + print('---- networkctl status %s ----' % self.iface) + sys.stdout.flush() + subprocess.call(['networkctl', 'status', self.iface]) + self.show_journal('systemd-networkd.service') + self.print_server_log() + raise + + # verify resolv.conf if it gets dynamically managed + if os.path.islink('/etc/resolv.conf'): + for timeout in range(50): + with open('/etc/resolv.conf') as f: + contents = f.read() + if 'nameserver 192.168.5.1\n' in contents: + break + # resolv.conf can have at most three nameservers; if we already + # have three different ones, that's also okay + if contents.count('nameserver ') >= 3: + break + time.sleep(0.1) + else: + self.fail('nameserver 192.168.5.1 not found in /etc/resolv.conf') + + if not coldplug: + # check post-down.d hook + self.shutdown_iface() + + def test_coldplug_dhcp_yes_ip4(self): + # we have a 12s timeout on RA, so we need to wait longer + self.do_test(coldplug=True, ipv6=False, online_timeout=15) + + def test_coldplug_dhcp_yes_ip4_no_ra(self): + # with disabling RA explicitly things should be fast + self.do_test(coldplug=True, ipv6=False, + extra_opts='IPv6AcceptRouterAdvertisements=False') + + def test_coldplug_dhcp_ip4_only(self): + # we have a 12s timeout on RA, so we need to wait longer + self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', + online_timeout=15) + + def test_coldplug_dhcp_ip4_only_no_ra(self): + # with disabling RA explicitly things should be fast + self.do_test(coldplug=True, ipv6=False, dhcp_mode='ipv4', + extra_opts='IPv6AcceptRouterAdvertisements=False') + + def test_coldplug_dhcp_ip6(self): + self.do_test(coldplug=True, ipv6=True) + + def test_hotplug_dhcp_ip4(self): + # With IPv4 only we have a 12s timeout on RA, so we need to wait longer + self.do_test(coldplug=False, ipv6=False, online_timeout=15) + + def test_hotplug_dhcp_ip6(self): + self.do_test(coldplug=False, ipv6=True) + + +@unittest.skipUnless(have_dnsmasq, 'dnsmasq not installed') +class DnsmasqClientTest(ClientTestBase, unittest.TestCase): + '''Test networkd client against dnsmasq''' + + def setUp(self): + super().setUp() + self.dnsmasq = None + + def create_iface(self, ipv6=False): + '''Create test interface with DHCP server behind it''' + + # add veth pair + subprocess.check_call(['ip', 'link', 'add', 'name', self.iface, 'type', + 'veth', 'peer', 'name', self.if_router]) + + # give our router an IP + subprocess.check_call(['ip', 'a', 'flush', 'dev', self.if_router]) + subprocess.check_call(['ip', 'a', 'add', '192.168.5.1/24', 'dev', self.if_router]) + if ipv6: + subprocess.check_call(['ip', 'a', 'add', '2600::1/64', 'dev', self.if_router]) + subprocess.check_call(['ip', 'link', 'set', self.if_router, 'up']) + + # add DHCP server + self.dnsmasq_log = os.path.join(self.workdir, 'dnsmasq.log') + lease_file = os.path.join(self.workdir, 'dnsmasq.leases') + if ipv6: + extra_opts = ['--enable-ra', '--dhcp-range=2600::10,2600::20'] + else: + extra_opts = [] + self.dnsmasq = subprocess.Popen( + ['dnsmasq', '--keep-in-foreground', '--log-queries', + '--log-facility=' + self.dnsmasq_log, '--conf-file=/dev/null', + '--dhcp-leasefile=' + lease_file, '--bind-interfaces', + '--interface=' + self.if_router, '--except-interface=lo', + '--dhcp-range=192.168.5.10,192.168.5.200'] + extra_opts) + + def shutdown_iface(self): + '''Remove test interface and stop DHCP server''' + + if self.if_router: + subprocess.check_call(['ip', 'link', 'del', 'dev', self.if_router]) + self.if_router = None + if self.dnsmasq: + self.dnsmasq.kill() + self.dnsmasq.wait() + self.dnsmasq = None + + def print_server_log(self): + '''Print DHCP server log for debugging failures''' + + with open(self.dnsmasq_log) as f: + sys.stdout.write('\n\n---- dnsmasq log ----\n%s\n------\n\n' % f.read()) + + +class NetworkdClientTest(ClientTestBase, unittest.TestCase): + '''Test networkd client against networkd server''' + + def setUp(self): + super().setUp() + self.dnsmasq = None + + def create_iface(self, ipv6=False): + '''Create test interface with DHCP server behind it''' + + # run "router-side" networkd in own mount namespace to shield it from + # "client-side" configuration and networkd + (fd, script) = tempfile.mkstemp(prefix='networkd-router.sh') + self.addCleanup(os.remove, script) + with os.fdopen(fd, 'w+') as f: + f.write('''#!/bin/sh -eu +mkdir -p /run/systemd/network +mkdir -p /run/systemd/netif +mount -t tmpfs none /run/systemd/network +mount -t tmpfs none /run/systemd/netif +[ ! -e /run/dbus ] || mount -t tmpfs none /run/dbus +# create router/client veth pair +cat << EOF > /run/systemd/network/test.netdev +[NetDev] +Name=%(ifr)s +Kind=veth + +[Peer] +Name=%(ifc)s +EOF + +cat << EOF > /run/systemd/network/test.network +[Match] +Name=%(ifr)s + +[Network] +Address=192.168.5.1/24 +%(addr6)s +DHCPServer=yes + +[DHCPServer] +PoolOffset=10 +PoolSize=50 +DNS=192.168.5.1 +EOF + +# run networkd as in systemd-networkd.service +exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ { s/^.*=//; p}') +''' % {'ifr': self.if_router, 'ifc': self.iface, 'addr6': ipv6 and 'Address=2600::1/64' or ''}) + + os.fchmod(fd, 0o755) + + subprocess.check_call(['systemd-run', '--unit=networkd-test-router.service', + '-p', 'InaccessibleDirectories=-/etc/systemd/network', + '-p', 'InaccessibleDirectories=-/run/systemd/network', + '-p', 'InaccessibleDirectories=-/run/systemd/netif', + '--service-type=notify', script]) + + # wait until devices got created + for timeout in range(50): + out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.if_router]) + if b'state UP' in out and b'scope global' in out: + break + time.sleep(0.1) + + def shutdown_iface(self): + '''Remove test interface and stop DHCP server''' + + if self.if_router: + subprocess.check_call(['systemctl', 'stop', 'networkd-test-router.service']) + # ensure failed transient unit does not stay around + subprocess.call(['systemctl', 'reset-failed', 'networkd-test-router.service']) + subprocess.call(['ip', 'link', 'del', 'dev', self.if_router]) + self.if_router = None + + def print_server_log(self): + '''Print DHCP server log for debugging failures''' + + self.show_journal('networkd-test-router.service') + + @unittest.skip('networkd does not have DHCPv6 server support') + def test_hotplug_dhcp_ip6(self): + pass + + @unittest.skip('networkd does not have DHCPv6 server support') + def test_coldplug_dhcp_ip6(self): + pass + + +if __name__ == '__main__': + unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, + verbosity=2)) |